diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/20-slice/index.html b/20-slice/index.html new file mode 100644 index 00000000..6f4c7308 --- /dev/null +++ b/20-slice/index.html @@ -0,0 +1,1186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Not understanding slice length and capacity (#20) - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + + + +
+
+ + + + + + + +

Not understanding slice length and capacity

+

+

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

+

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

+

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

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

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

+
+

+

+
Figure 1: A three-length, six-capacity slice.
+
+

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

+

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

+
+

+

+
Figure 2: Updating the slice’s second element: s[1] = 1.
+
+

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

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

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

+
s = append(s, 2)
+
+

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

+
+

+

+
Figure 3: Appending an element to s.
+
+

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

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

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

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

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

+
+

+

+
Figure 4: Because the initial backing array is full, Go creates another array and copies all the elements.
+
+

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

+

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

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

+

+
Figure 5: The slices s1 and s2 reference the same backing array with different lengths and capacities.
+
+

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

+
+

+

+
Figure 6: Because s1 and s2 are backed by the same array, updating a common element makes the change visible in both slices.
+
+

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

+
s2 = append(s2, 2)
+
+

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

+
+

+

+
Figure 7: Appending an element to s2.
+
+

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

+
s1=[0 1 0], s2=[1 0 2]
+
+

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

+
+Note +

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

+
+

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

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

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

+
+

+

+
Figure 8: Appending elements to s2 until the backing array is full.
+
+

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

+

To summarize, the slice length is the number of available elements in the slice, whereas the slice capacity is the number of elements in the backing array. Adding an element to a full slice (length == capacity) leads to creating a new backing array with a new capacity, copying all the elements from the previous array, and updating the slice pointer to the new array.

+ + + + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/28-maps-memory-leaks/index.html b/28-maps-memory-leaks/index.html new file mode 100644 index 00000000..78834a4a --- /dev/null +++ b/28-maps-memory-leaks/index.html @@ -0,0 +1,1197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Maps and memory leaks (#28) - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + + + +
+
+ + + + + + + +

Maps and memory leaks

+

+

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

+

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

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

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

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

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

+
func main() {
+    n := 1_000_000
+    m := make(map[int][128]byte)
+    printAlloc()
+
+    for i := 0; i < n; i++ { // Adds 1 million elements
+        m[i] = [128]byte{}
+    }
+    printAlloc()
+
+    for i := 0; i < n; i++ { // Deletes 1 million elements
+        delete(m, i)
+    }
+
+    runtime.GC() // Triggers a manual GC
+    printAlloc()
+    runtime.KeepAlive(m) // Keeps a reference to m so that the map isn’t collected
+}
+
+func printAlloc() {
+    var m runtime.MemStats
+    runtime.ReadMemStats(&m)
+    fmt.Printf("%d MB\n", m.Alloc/(1024*1024))
+}
+
+

We allocate an empty map, add 1 million elements, remove 1 million elements, and then run a GC. We also make sure to keep a reference to the map using runtime.KeepAlive so that the map isn’t collected as well. Let’s run this example:

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

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

+

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

+
+

+

+
Figure 1: A hash table example with a focus on bucket 0.
+
+

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

+
+

+

+
Figure 2: In case of a bucket overflow, Go allocates a new bucket and links the previous bucket to it.
+
+

Under the hood, a Go map is a pointer to a runtime.hmap struct. This struct contains multiple fields, including a B field, giving the number of buckets in the map:

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

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

+

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

+

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

+

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

+

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

+

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

+

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

+

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

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Stepmap[int][128]bytemap[int]*[128]byte
Allocate an empty map0 MB0 MB
Add 1 million elements461 MB182 MB
Remove all the elements and run a GC293 MB38 MB
+
+Note +

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

+
+

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

+ + + + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/404.html b/404.html new file mode 100644 index 00000000..18baa865 --- /dev/null +++ b/404.html @@ -0,0 +1,945 @@ + + + + + + + + + + + + + + + + + + + 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/5-interface-pollution/index.html b/5-interface-pollution/index.html new file mode 100644 index 00000000..d03cb200 --- /dev/null +++ b/5-interface-pollution/index.html @@ -0,0 +1,1446 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Interface pollution (#5) - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + + + +
+
+ + + + + + + +

Interface pollution

+

+

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

+

Concepts

+

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

+

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

+
+

+
+

The io.Reader contains a single Read method:

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

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

+

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

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

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

+
    +
  • io.Reader reads data from a source
  • +
  • io.Writer writes data to a target
  • +
+

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

+

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

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

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

+

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

+
func TestCopySourceToDest(t *testing.T) {
+    const input = "foo"
+    source := strings.NewReader(input) // Creates an io.Reader
+    dest := bytes.NewBuffer(make([]byte, 0)) // Creates an io.Writer
+
+    err := copySourceToDest(source, dest) // Calls copySourceToDest from a *strings.Reader and a *bytes.Buffer
+    if err != nil {
+        t.FailNow()
+    }
+
+    got := dest.String()
+    if got != input {
+        t.Errorf("expected: %s, got: %s", input, got)
+    }
+}
+
+

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

+

While designing interfaces, the granularity (how many methods the interface contains) is also something to keep in mind. A known proverb in Go relates to how big an interface should be:

+
+

Rob Pike

+

The bigger the interface, the weaker the abstraction.

+
+

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

+
type ReadWriter interface {
+    Reader
+    Writer
+}
+
+
+Note +

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

+
+

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

+

When to use interfaces

+

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

+
    +
  • Common behavior
  • +
  • Decoupling
  • +
  • Restricting behavior
  • +
+

Common behavior

+

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

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

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

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

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

+

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

+

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

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

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

+

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

+

Decoupling

+

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

+

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

+
type CustomerService struct {
+    store mysql.Store // Depends on the concrete implementation
+}
+
+func (cs CustomerService) CreateNewCustomer(id string) error {
+    customer := Customer{id: id}
+    return cs.store.StoreCustomer(customer)
+}
+
+

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

+
type customerStorer interface { // Creates a storage abstraction
+    StoreCustomer(Customer) error
+}
+
+type CustomerService struct {
+    storer customerStorer // Decouples CustomerService from the actual implementation
+}
+
+func (cs CustomerService) CreateNewCustomer(id string) error {
+    customer := Customer{id: id}
+    return cs.storer.StoreCustomer(customer)
+}
+
+

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

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

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

+

Restricting behavior

+

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

+
type IntConfig struct {
+    // ...
+}
+
+func (c *IntConfig) Get() int {
+    // Retrieve configuration
+}
+
+func (c *IntConfig) Set(value int) {
+    // Update configuration
+}
+
+

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

+
type intConfigGetter interface {
+    Get() int
+}
+
+

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

+
type Foo struct {
+    threshold intConfigGetter
+}
+
+func NewFoo(threshold intConfigGetter) Foo { // Injects the configuration getter
+    return Foo{threshold: threshold}
+}
+
+func (f Foo) Bar()  {
+    threshold := f.threshold.Get() // Reads the configuration
+    // ...
+}
+
+

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

+

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

+

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

+

Interface pollution

+

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

+

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

+

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

+
+Note +

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

+
+

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

+
+

Rob Pike

+

Don’t design with interfaces, discover them.

+
+

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

+ + + + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/56-concurrency-faster/index.html b/56-concurrency-faster/index.html new file mode 100644 index 00000000..7b0b105c --- /dev/null +++ b/56-concurrency-faster/index.html @@ -0,0 +1,1348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Thinking concurrency is always faster (#56) - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + + + +
+
+ + + + + + + +

Thinking concurrency is always faster

+

+

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

+

Go Scheduling

+

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

+
    +
  • Concurrent — Two or more threads can start, run, and complete in overlapping time periods.
  • +
  • Parallel — The same task can be executed multiple times at once.
  • +
+

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

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

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

+
+

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

+

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

+
+Note +

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

+
+

Let’s now discuss how the Go scheduler works to overview how goroutines are handled. Internally, the Go scheduler uses the following terminology (see proc.go):

+
    +
  • G — Goroutine
  • +
  • M — OS thread (stands for machine)
  • +
  • P — CPU core (stands for processor)
  • +
+

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

+

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

+
    +
  • Executing — The goroutine is scheduled on an M and executing its instructions.
  • +
  • Runnable — The goroutine is waiting to be in an executing state.
  • +
  • Waiting — The goroutine is stopped and pending something completing, such as a system call or a synchronization operation (such as acquiring a mutex).
  • +
+

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

+

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

+
+

+

+
Figure 1: An example of the current state of a Go application executed on a four-core machine. Goroutines that aren’t in an executing state are either runnable (pending being executed) or waiting (pending a blocking operation)
+
+

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

+

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

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

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

+

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

+

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

+

Parallel Merge Sort

+

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

+

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

+
+

+

+
Figure 2: Applying the merge sort algorithm repeatedly breaks each list into two sublists. Then the algorithm uses a merge operation such that the resulting list is sorted
+
+

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

+
func sequentialMergesort(s []int) {
+    if len(s) <= 1 {
+        return
+    }
+
+    middle := len(s) / 2
+    sequentialMergesort(s[:middle]) // First half
+    sequentialMergesort(s[middle:]) // Second half
+    merge(s, middle) // Merges the two halves
+}
+
+func merge(s []int, middle int) {
+    // ...
+}
+
+

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

+
func parallelMergesortV1(s []int) {
+    if len(s) <= 1 {
+        return
+    }
+
+    middle := len(s) / 2
+
+    var wg sync.WaitGroup
+    wg.Add(2)
+
+    go func() { // Spins up the first half of the work in a goroutine
+        defer wg.Done()
+        parallelMergesortV1(s[:middle])
+    }()
+
+    go func() { // Spins up the second half of the work in a goroutine
+        defer wg.Done()
+        parallelMergesortV1(s[middle:])
+    }()
+
+    wg.Wait()
+    merge(s, middle) // Merges the halves
+}
+
+

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

+

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

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

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

+

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

+

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

+

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

+

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

+
const max = 2048 // Defines the threshold
+
+func parallelMergesortV2(s []int) {
+    if len(s) <= 1 {
+        return
+    }
+
+    if len(s) <= max {
+        sequentialMergesort(s) // Calls our initial sequential version
+    } else { // If bigger than the threshold, keeps the parallel version
+        middle := len(s) / 2
+
+        var wg sync.WaitGroup
+        wg.Add(2)
+
+        go func() {
+            defer wg.Done()
+            parallelMergesortV2(s[:middle])
+        }()
+
+        go func() {
+            defer wg.Done()
+            parallelMergesortV2(s[middle:])
+        }()
+
+        wg.Wait()
+        merge(s, middle)
+    }
+}
+
+

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

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

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

+
+Note +

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

+
+

Conclusion

+

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

+

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

+

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

+ + + + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/89-benchmarks/index.html b/89-benchmarks/index.html new file mode 100644 index 00000000..7f6703fb --- /dev/null +++ b/89-benchmarks/index.html @@ -0,0 +1,1490 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Writing inaccurate benchmarks (#89) - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + + + +
+
+ + + + + + + +

Writing inaccurate benchmarks

+

+

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

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

General concepts

+

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

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

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

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

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

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

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

+

Next, let’s look at some common traps.

+

Not resetting or pausing the timer

+

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

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

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

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

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

+

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

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

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

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

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

+
+Note +

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

+
+

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

+

Making wrong assumptions about micro-benchmarks

+

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

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

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

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

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

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

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

+

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

+
+Note +

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

+
+

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

+

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

+

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

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

We can then run benchstat on this file:

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

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

+

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

+

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

+

Not being careful about compiler optimizations

+

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

+
const m1 = 0x5555555555555555
+const m2 = 0x3333333333333333
+const m4 = 0x0f0f0f0f0f0f0f0f
+const h01 = 0x0101010101010101
+
+func popcnt(x uint64) uint64 {
+    x -= (x >> 1) & m1
+    x = (x & m2) + ((x >> 2) & m2)
+    x = (x + (x >> 4)) & m4
+    return (x * h01) >> 56
+}
+
+

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

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

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

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

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

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

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

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

In our case, we write the following benchmark:

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

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

+
+Note +

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

+
+

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

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

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

+

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

+

Being fooled by the observer effect

+

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

+

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

+
+

+

+
Figure 1: Computing the sum of the first eight columns.
+
+

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

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

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

+

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

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

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

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

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

+

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

+
+Note +

This isn’t in the scope of this post to explain why, but we look at this problem in 100 Go Mistakes, mistake #91: “Not understanding CPU caches.

+
+

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

+

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

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

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

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

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

+

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

+ + + + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/9-generics/index.html b/9-generics/index.html new file mode 100644 index 00000000..a5228ea2 --- /dev/null +++ b/9-generics/index.html @@ -0,0 +1,1347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Being confused about when to use generics (#9) - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + + + +
+
+ + + + + + + +

Being confused about when to use generics

+

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

+

Concepts

+

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

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

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

+

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

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

We can start noticing a couple of issues:

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

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

+

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

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

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

+

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

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

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

+
var m map[[]byte]int
+
+

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

+

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

+
    +
  • A set of behaviors (methods)
  • +
  • But also arbitrary type
  • +
+

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

+
type customConstraint interface {
+   ~int | ~string // Define a custom type that will restrict types to int and string
+}
+
+// Change the type parameter K to be custom
+func getKeys[K customConstraint, V any](m map[K]V) []K {
+   // Same implementation
+}
+
+

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

+

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

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

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

+
keys := getKeys[string](m)
+
+
+Note +

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

+

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

+
type customConstraint interface {
+   ~int
+   String() string
+}
+
+

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

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

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

+

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

+
+

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

+

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

+

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

+
type Node[T any] struct { // Use type parameter
+   Val  T
+   next *Node[T]
+}
+
+func (n *Node[T]) Add(next *Node[T]) { // Instantiate type receiver
+   n.next = next
+}
+
+

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

+

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

+
type Foo struct {}
+
+func (Foo) bar[T any](t T) {}
+
+
./main.go:29:15: methods cannot have type parameters
+
+

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

+

Common uses and misuses

+

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

+
    +
  • Data structures. For example, we can use generics to factor out the element type if we implement a binary tree, a linked list, or a heap.
  • +
  • Functions working with slices, maps, and channels of any type. For example, a function to merge two channels would work with any channel type. Hence, we could use type parameters to factor out the channel type:
  • +
+
func merge[T any](ch1, ch2 <-chan T) <-chan T {
+    // ...
+}
+
+
    +
  • Meanwhile, instead of factoring out a type, we can factor out behaviors. For example, the sort package contains functions to sort different slice types such as sort.Ints or sort.Float64s. Using type parameters, we can factor out the sorting behaviors that rely on three methods, Len, Less, and Swap:
  • +
+
type sliceFn[T any] struct { // Use type parameter
+   s       []T
+   compare func(T, T) bool // Compare two T elements
+}
+
+func (s sliceFn[T]) Len() int           { return len(s.s) }
+func (s sliceFn[T]) Less(i, j int) bool { return s.compare(s.s[i], s.s[j]) }
+func (s sliceFn[T]) Swap(i, j int)      { s.s[i], s.s[j] = s.s[j], s.s[i] }
+
+

Conversely, when is it recommended not to use generics?

+
    +
  • When just calling a method of the type argument. For example, consider a function that receives an io.Writer and call the Write method:
  • +
+
func foo[T io.Writer](w T) {
+   b := getBytes()
+   _, _ = w.Write(b)
+}
+
+
    +
  • When it makes our code more complex. Generics are never mandatory, and as Go developers, we have been able to live without them for more than a decade. If writing generic functions or structures we figure out that it doesn’t make our code clearer, we should probably reconsider our decision for this particular use case.
  • +
+

Conclusion

+

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

+

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

+

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

+ + + + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/92-false-sharing/index.html b/92-false-sharing/index.html new file mode 100644 index 00000000..6f291ce5 --- /dev/null +++ b/92-false-sharing/index.html @@ -0,0 +1,1183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Writing concurrent code that leads to false sharing (#92) - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + + + +
+
+ + + + + + + +

Writing concurrent code that leads to false sharing

+

+

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

+

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

+
type Input struct {
+    a int64
+    b int64
+}
+
+type Result struct {
+    sumA int64
+    sumB int64
+}
+
+

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

+
    +
  • The sum of all the Input.a fields into Result.sumA
  • +
  • The sum of all the Input.b fields into Result.sumB
  • +
+

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

+
func count(inputs []Input) Result {
+    wg := sync.WaitGroup{}
+    wg.Add(2)
+
+    result := Result{} // Init the result struct
+
+    go func() {
+        for i := 0; i < len(inputs); i++ {
+            result.sumA += inputs[i].a // Computes sumA
+        }
+        wg.Done()
+    }()
+
+    go func() {
+        for i := 0; i < len(inputs); i++ {
+            result.sumB += inputs[i].b // Computes sumB
+        }
+        wg.Done()
+    }()
+
+    wg.Wait()
+    return result
+}
+
+

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

+

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

+
+

+

+
In this example, sumA and sumB are part of the same memory block.
+
+

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

+
+

+

+
Each block is copied to a cache line on both code 0 and core 1.
+
+

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

+
+

+

+
Each goroutine updates its own variable.
+
+

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

+

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

+

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

+
+Note +

Internally, a CPU uses the MESI protocol to guarantee cache coherency. It tracks each cache line, marking it modified, exclusive, shared, or invalid (MESI).

+
+

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

+

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

+

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

+
type Result struct {
+    sumA int64
+    _    [56]byte // Padding
+    sumB int64
+}
+
+

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

+
+

+

+
sumA and sumB are part of different memory blocks.
+
+

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

+

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

+

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

+ + + + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/98-profiling-execution-tracing/index.html b/98-profiling-execution-tracing/index.html new file mode 100644 index 00000000..69449f98 --- /dev/null +++ b/98-profiling-execution-tracing/index.html @@ -0,0 +1,1536 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Not using Go diagnostics tooling (#98) - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + + + +
+
+ + + + + + + +

Not using Go diagnostics tooling

+

+

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

+

Profiling

+

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

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

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

+

Enabling pprof

+

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

+
package main
+
+import (
+    "fmt"
+    "log"
+    "net/http"
+    _ "net/http/pprof" // Blank import to pprof
+)
+
+func main() {
+    // Exposes an HTTP endpoint
+    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+        fmt.Fprintf(w, "")
+    })
+    log.Fatal(http.ListenAndServe(":80", nil))
+}
+
+

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

+

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

+

CPU Profiling

+

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

+

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

+
+Note +

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

+
$ go test -bench=. -cpuprofile profile.out
+
+
+

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

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

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

+
+

+

+
Figure 1: The call graph of an application during 30 seconds.
+
+

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

+
+

+

+
Figure 2: Example call graph.
+
+

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

+
+Note +

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

+
+

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

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

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

+
+Note +

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

+
+

Heap Profiling

+

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

+

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

+

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

+
+

+

+
Figure 3: A heap graph.
+
+

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

+
    +
  • alloc_objects— Total number of objects allocated
  • +
  • alloc_space— Total amount of memory allocated
  • +
  • inuse_objects — Number of objects allocated and not yet released
  • +
  • inuse_space— Amount of memory allocated and not yet released
  • +
+

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

+
    +
  1. Trigger a GC.
  2. +
  3. Download heap data.
  4. +
  5. Wait for a few seconds/minutes.
  6. +
  7. Trigger another GC.
  8. +
  9. Download another heap data.
  10. +
  11. Compare.
  12. +
+

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

+

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

+
    +
  1. Go to /debug/pprof/heap?gc=1 (trigger the GC and download the heap profile).
  2. +
  3. Wait for a few seconds/minutes.
  4. +
  5. Go to /debug/pprof/heap?gc=1 again.
  6. +
  7. Use go tool to compare both heap profiles:
  8. +
+
$ go tool pprof -http=:8080 -diff_base <file2> <file1>
+
+

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

+
+

+

+
Figure 4: The differences between the two heap profiles.
+
+
+Note +

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

+
+

Goroutine Profiling

+

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

+
+

+

+
Figure 5: Goroutine graph.
+
+

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

+

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

+

Block Profiling

+

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

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

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

+

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

+
+Note +

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

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

Mutex Profiling

+

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

+

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

+

Following are a few additional notes about profiling:

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

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

+

Let’s now look at the execution tracer.

+

Execution Tracer

+

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

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

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

+

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

+
$ go test -bench=. -v -trace=trace.out
+
+
+Note +

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

+
+

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

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

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

+
+

+

+
Figure 6: Showing goroutine activity and runtime events such as a GC phase.
+
+

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

+
+

+

+
Figure 7: Too many small bars mean poorly parallelized execution.
+
+

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

+
+

+

+
Figure 8: About 50% of CPU time is spent handling goroutine switches.
+
+

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

+
+

+

+
Figure 9: The number of white spaces has been significantly reduced, proving that the CPU is more fully occupied.
+
+

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

+

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

+

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

+
var v int64
+// Creates a fibonacci task
+ctx, fibTask := trace.NewTask(context.Background(), "fibonacci")
+trace.WithRegion(ctx, "main", func() {
+    v = fibonacci(10)
+})
+fibTask.End()
+
+// Creates a store task
+ctx, fibStore := trace.NewTask(ctx, "store")
+trace.WithRegion(ctx, "main", func() {
+    atomic.StoreInt64(&result, v)
+})
+fibStore.End()
+
+

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

+
+

+

+
Figure 10: Distribution of user-level tasks.
+
+

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

+

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

+
    +
  • CPU profiling:
      +
    • Sample-based
    • +
    • Per function
    • +
    • Doesn’t go below the sampling rate (10 ms by default)
    • +
    +
  • +
  • User-level traces:
      +
    • Not sample-based
    • +
    • Per-goroutine execution (unless we use the runtime/trace package)
    • +
    • Time executions aren’t bound by any rate
    • +
    +
  • +
+

In summary, the execution tracer is a powerful tool for understanding how an application performs. As we have seen with the merge sort example, we can identify poorly parallelized execution. However, the tracer’s granularity remains per goroutine unless we manually use runtime/trace compared to a CPU profile, for example. We can use both profiling and the execution tracer to get the most out of the standard Go diagnostics tools when optimizing an application.

+ + + + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 00000000..7144cc49 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +100go.co diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 00000000..1cf13b9f Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/images/social/20-slice.png b/assets/images/social/20-slice.png new file mode 100644 index 00000000..c36ca118 Binary files /dev/null and b/assets/images/social/20-slice.png differ diff --git a/assets/images/social/28-maps-memory-leaks.png b/assets/images/social/28-maps-memory-leaks.png new file mode 100644 index 00000000..eae2887f Binary files /dev/null and b/assets/images/social/28-maps-memory-leaks.png differ diff --git a/assets/images/social/5-interface-pollution.png b/assets/images/social/5-interface-pollution.png new file mode 100644 index 00000000..df24146c Binary files /dev/null and b/assets/images/social/5-interface-pollution.png differ diff --git a/assets/images/social/56-concurrency-faster.png b/assets/images/social/56-concurrency-faster.png new file mode 100644 index 00000000..a82d09f1 Binary files /dev/null and b/assets/images/social/56-concurrency-faster.png differ diff --git a/assets/images/social/89-benchmarks.png b/assets/images/social/89-benchmarks.png new file mode 100644 index 00000000..1730d932 Binary files /dev/null and b/assets/images/social/89-benchmarks.png differ diff --git a/assets/images/social/9-generics.png b/assets/images/social/9-generics.png new file mode 100644 index 00000000..0049e174 Binary files /dev/null and b/assets/images/social/9-generics.png differ diff --git a/assets/images/social/92-false-sharing.png b/assets/images/social/92-false-sharing.png new file mode 100644 index 00000000..bd2354a3 Binary files /dev/null and b/assets/images/social/92-false-sharing.png differ diff --git a/assets/images/social/98-profiling-execution-tracing.png b/assets/images/social/98-profiling-execution-tracing.png new file mode 100644 index 00000000..87c7388d Binary files /dev/null and b/assets/images/social/98-profiling-execution-tracing.png differ diff --git a/assets/images/social/book.png b/assets/images/social/book.png new file mode 100644 index 00000000..214791ee Binary files /dev/null and b/assets/images/social/book.png differ diff --git a/assets/images/social/chapter-1.png b/assets/images/social/chapter-1.png new file mode 100644 index 00000000..e580f835 Binary files /dev/null and b/assets/images/social/chapter-1.png differ diff --git a/assets/images/social/external.png b/assets/images/social/external.png new file mode 100644 index 00000000..df53383a Binary files /dev/null and b/assets/images/social/external.png differ diff --git a/assets/images/social/index.png b/assets/images/social/index.png new file mode 100644 index 00000000..4e2717bc Binary files /dev/null and b/assets/images/social/index.png differ diff --git a/assets/images/social/ja.png b/assets/images/social/ja.png new file mode 100644 index 00000000..d668daf4 Binary files /dev/null and b/assets/images/social/ja.png differ diff --git a/assets/images/social/zh.png b/assets/images/social/zh.png new file mode 100644 index 00000000..e5f0335a Binary files /dev/null and b/assets/images/social/zh.png differ diff --git a/assets/javascripts/bundle.c8d2eff1.min.js b/assets/javascripts/bundle.c8d2eff1.min.js new file mode 100644 index 00000000..4b1b31f5 --- /dev/null +++ b/assets/javascripts/bundle.c8d2eff1.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var _i=Object.create;var br=Object.defineProperty;var Ai=Object.getOwnPropertyDescriptor;var Ci=Object.getOwnPropertyNames,Ft=Object.getOwnPropertySymbols,ki=Object.getPrototypeOf,vr=Object.prototype.hasOwnProperty,eo=Object.prototype.propertyIsEnumerable;var Zr=(e,t,r)=>t in e?br(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,F=(e,t)=>{for(var r in t||(t={}))vr.call(t,r)&&Zr(e,r,t[r]);if(Ft)for(var r of Ft(t))eo.call(t,r)&&Zr(e,r,t[r]);return e};var to=(e,t)=>{var r={};for(var o in e)vr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Ft)for(var o of Ft(e))t.indexOf(o)<0&&eo.call(e,o)&&(r[o]=e[o]);return r};var gr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Hi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ci(t))!vr.call(e,n)&&n!==r&&br(e,n,{get:()=>t[n],enumerable:!(o=Ai(t,n))||o.enumerable});return e};var jt=(e,t,r)=>(r=e!=null?_i(ki(e)):{},Hi(t||!e||!e.__esModule?br(r,"default",{value:e,enumerable:!0}):r,e));var ro=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var no=gr((xr,oo)=>{(function(e,t){typeof xr=="object"&&typeof oo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(C){return!!(C&&C!==document&&C.nodeName!=="HTML"&&C.nodeName!=="BODY"&&"classList"in C&&"contains"in C.classList)}function c(C){var ct=C.type,Ne=C.tagName;return!!(Ne==="INPUT"&&s[ct]&&!C.readOnly||Ne==="TEXTAREA"&&!C.readOnly||C.isContentEditable)}function p(C){C.classList.contains("focus-visible")||(C.classList.add("focus-visible"),C.setAttribute("data-focus-visible-added",""))}function l(C){C.hasAttribute("data-focus-visible-added")&&(C.classList.remove("focus-visible"),C.removeAttribute("data-focus-visible-added"))}function f(C){C.metaKey||C.altKey||C.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(C){o=!1}function h(C){a(C.target)&&(o||c(C.target))&&p(C.target)}function w(C){a(C.target)&&(C.target.classList.contains("focus-visible")||C.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(C.target))}function A(C){document.visibilityState==="hidden"&&(n&&(o=!0),Z())}function Z(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function te(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(C){C.target.nodeName&&C.target.nodeName.toLowerCase()==="html"||(o=!1,te())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",A,!0),Z(),r.addEventListener("focus",h,!0),r.addEventListener("blur",w,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var zr=gr((kt,Vr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof kt=="object"&&typeof Vr=="object"?Vr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof kt=="object"?kt.ClipboardJS=r():t.ClipboardJS=r()})(kt,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Li}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(D){try{return document.execCommand(D)}catch(M){return!1}}var h=function(M){var O=f()(M);return u("cut"),O},w=h;function A(D){var M=document.documentElement.getAttribute("dir")==="rtl",O=document.createElement("textarea");O.style.fontSize="12pt",O.style.border="0",O.style.padding="0",O.style.margin="0",O.style.position="absolute",O.style[M?"right":"left"]="-9999px";var I=window.pageYOffset||document.documentElement.scrollTop;return O.style.top="".concat(I,"px"),O.setAttribute("readonly",""),O.value=D,O}var Z=function(M,O){var I=A(M);O.container.appendChild(I);var W=f()(I);return u("copy"),I.remove(),W},te=function(M){var O=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},I="";return typeof M=="string"?I=Z(M,O):M instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(M==null?void 0:M.type)?I=Z(M.value,O):(I=f()(M),u("copy")),I},J=te;function C(D){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?C=function(O){return typeof O}:C=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},C(D)}var ct=function(){var M=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},O=M.action,I=O===void 0?"copy":O,W=M.container,K=M.target,Ce=M.text;if(I!=="copy"&&I!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(K!==void 0)if(K&&C(K)==="object"&&K.nodeType===1){if(I==="copy"&&K.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(I==="cut"&&(K.hasAttribute("readonly")||K.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Ce)return J(Ce,{container:W});if(K)return I==="cut"?w(K):J(K,{container:W})},Ne=ct;function Pe(D){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Pe=function(O){return typeof O}:Pe=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},Pe(D)}function xi(D,M){if(!(D instanceof M))throw new TypeError("Cannot call a class as a function")}function Xr(D,M){for(var O=0;O0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof W.action=="function"?W.action:this.defaultAction,this.target=typeof W.target=="function"?W.target:this.defaultTarget,this.text=typeof W.text=="function"?W.text:this.defaultText,this.container=Pe(W.container)==="object"?W.container:document.body}},{key:"listenClick",value:function(W){var K=this;this.listener=p()(W,"click",function(Ce){return K.onClick(Ce)})}},{key:"onClick",value:function(W){var K=W.delegateTarget||W.currentTarget,Ce=this.action(K)||"copy",It=Ne({action:Ce,container:this.container,target:this.target(K),text:this.text(K)});this.emit(It?"success":"error",{action:Ce,text:It,trigger:K,clearSelection:function(){K&&K.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(W){return hr("action",W)}},{key:"defaultTarget",value:function(W){var K=hr("target",W);if(K)return document.querySelector(K)}},{key:"defaultText",value:function(W){return hr("text",W)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(W){var K=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(W,K)}},{key:"cut",value:function(W){return w(W)}},{key:"isSupported",value:function(){var W=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],K=typeof W=="string"?[W]:W,Ce=!!document.queryCommandSupported;return K.forEach(function(It){Ce=Ce&&!!document.queryCommandSupported(It)}),Ce}}]),O}(a()),Li=Mi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s},438:function(o,n,i){var s=i(828);function a(l,f,u,h,w){var A=p.apply(this,arguments);return l.addEventListener(u,A,w),{destroy:function(){l.removeEventListener(u,A,w)}}}function c(l,f,u,h,w){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(A){return a(A,f,u,h,w)}))}function p(l,f,u,h){return function(w){w.delegateTarget=s(w.target,f),w.delegateTarget&&h.call(l,w)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(o,n,i){var s=i(879),a=i(438);function c(u,h,w){if(!u&&!h&&!w)throw new Error("Missing required arguments");if(!s.string(h))throw new TypeError("Second argument must be a String");if(!s.fn(w))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,h,w);if(s.nodeList(u))return l(u,h,w);if(s.string(u))return f(u,h,w);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,h,w){return u.addEventListener(h,w),{destroy:function(){u.removeEventListener(h,w)}}}function l(u,h,w){return Array.prototype.forEach.call(u,function(A){A.addEventListener(h,w)}),{destroy:function(){Array.prototype.forEach.call(u,function(A){A.removeEventListener(h,w)})}}}function f(u,h,w){return a(document.body,u,h,w)}o.exports=c},817:function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var Va=/["'&<>]/;qn.exports=za;function za(e){var t=""+e,r=Va.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function V(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function z(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||a(u,h)})})}function a(u,h){try{c(o[u](h))}catch(w){f(i[0][3],w)}}function c(u){u.value instanceof ot?Promise.resolve(u.value.v).then(p,l):f(i[0][2],u)}function p(u){a("next",u)}function l(u){a("throw",u)}function f(u,h){u(h),i.shift(),i.length&&a(i[0][0],i[0][1])}}function so(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof ue=="function"?ue(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function k(e){return typeof e=="function"}function pt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Wt=pt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=ue(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(A){t={error:A}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(k(l))try{l()}catch(A){i=A instanceof Wt?A.errors:[A]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=ue(f),h=u.next();!h.done;h=u.next()){var w=h.value;try{co(w)}catch(A){i=i!=null?i:[],A instanceof Wt?i=z(z([],V(i)),V(A.errors)):i.push(A)}}}catch(A){o={error:A}}finally{try{h&&!h.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Wt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)co(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Er=Ie.EMPTY;function Dt(e){return e instanceof Ie||e&&"closed"in e&&k(e.remove)&&k(e.add)&&k(e.unsubscribe)}function co(e){k(e)?e():e.unsubscribe()}var ke={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var lt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?Er:(this.currentObservers=null,a.push(r),new Ie(function(){o.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new j;return r.source=this,r},t.create=function(r,o){return new vo(r,o)},t}(j);var vo=function(e){se(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Er},t}(v);var St={now:function(){return(St.delegate||Date).now()},delegate:void 0};var Ot=function(e){se(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=St);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(ut.cancelAnimationFrame(o),r._scheduled=void 0)},t}(zt);var yo=function(e){se(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(qt);var de=new yo(xo);var L=new j(function(e){return e.complete()});function Kt(e){return e&&k(e.schedule)}function _r(e){return e[e.length-1]}function Je(e){return k(_r(e))?e.pop():void 0}function Ae(e){return Kt(_r(e))?e.pop():void 0}function Qt(e,t){return typeof _r(e)=="number"?e.pop():t}var dt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Yt(e){return k(e==null?void 0:e.then)}function Bt(e){return k(e[ft])}function Gt(e){return Symbol.asyncIterator&&k(e==null?void 0:e[Symbol.asyncIterator])}function Jt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Di(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Xt=Di();function Zt(e){return k(e==null?void 0:e[Xt])}function er(e){return ao(this,arguments,function(){var r,o,n,i;return Ut(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,ot(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,ot(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,ot(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function tr(e){return k(e==null?void 0:e.getReader)}function N(e){if(e instanceof j)return e;if(e!=null){if(Bt(e))return Ni(e);if(dt(e))return Vi(e);if(Yt(e))return zi(e);if(Gt(e))return Eo(e);if(Zt(e))return qi(e);if(tr(e))return Ki(e)}throw Jt(e)}function Ni(e){return new j(function(t){var r=e[ft]();if(k(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Vi(e){return new j(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):ce,ye(1),r?Qe(t):jo(function(){return new or}))}}function $r(e){return e<=0?function(){return L}:x(function(t,r){var o=[];t.subscribe(S(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new v}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,h=0,w=!1,A=!1,Z=function(){f==null||f.unsubscribe(),f=void 0},te=function(){Z(),l=u=void 0,w=A=!1},J=function(){var C=l;te(),C==null||C.unsubscribe()};return x(function(C,ct){h++,!A&&!w&&Z();var Ne=u=u!=null?u:r();ct.add(function(){h--,h===0&&!A&&!w&&(f=Pr(J,c))}),Ne.subscribe(ct),!l&&h>0&&(l=new it({next:function(Pe){return Ne.next(Pe)},error:function(Pe){A=!0,Z(),f=Pr(te,n,Pe),Ne.error(Pe)},complete:function(){w=!0,Z(),f=Pr(te,s),Ne.complete()}}),N(C).subscribe(l))})(p)}}function Pr(e,t){for(var r=[],o=2;oe.next(document)),e}function R(e,t=document){return Array.from(t.querySelectorAll(e))}function P(e,t=document){let r=me(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function me(e,t=document){return t.querySelector(e)||void 0}function Re(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var la=T(d(document.body,"focusin"),d(document.body,"focusout")).pipe(be(1),q(void 0),m(()=>Re()||document.body),B(1));function vt(e){return la.pipe(m(t=>e.contains(t)),Y())}function Vo(e,t){return T(d(e,"mouseenter").pipe(m(()=>!0)),d(e,"mouseleave").pipe(m(()=>!1))).pipe(t?be(t):ce,q(!1))}function Ue(e){return{x:e.offsetLeft,y:e.offsetTop}}function zo(e){return T(d(window,"load"),d(window,"resize")).pipe(Me(0,de),m(()=>Ue(e)),q(Ue(e)))}function ir(e){return{x:e.scrollLeft,y:e.scrollTop}}function et(e){return T(d(e,"scroll"),d(window,"resize")).pipe(Me(0,de),m(()=>ir(e)),q(ir(e)))}function qo(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)qo(e,r)}function E(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)qo(o,n);return o}function ar(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function gt(e){let t=E("script",{src:e});return H(()=>(document.head.appendChild(t),T(d(t,"load"),d(t,"error").pipe(b(()=>Ar(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),_(()=>document.head.removeChild(t)),ye(1))))}var Ko=new v,ma=H(()=>typeof ResizeObserver=="undefined"?gt("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>{for(let t of e)Ko.next(t)})),b(e=>T(qe,$(e)).pipe(_(()=>e.disconnect()))),B(1));function pe(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Ee(e){return ma.pipe(y(t=>t.observe(e)),b(t=>Ko.pipe(g(({target:r})=>r===e),_(()=>t.unobserve(e)),m(()=>pe(e)))),q(pe(e)))}function xt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function sr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var Qo=new v,fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)Qo.next(t)},{threshold:0}))).pipe(b(e=>T(qe,$(e)).pipe(_(()=>e.disconnect()))),B(1));function yt(e){return fa.pipe(y(t=>t.observe(e)),b(t=>Qo.pipe(g(({target:r})=>r===e),_(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function Yo(e,t=16){return et(e).pipe(m(({y:r})=>{let o=pe(e),n=xt(e);return r>=n.height-o.height-t}),Y())}var cr={drawer:P("[data-md-toggle=drawer]"),search:P("[data-md-toggle=search]")};function Bo(e){return cr[e].checked}function Be(e,t){cr[e].checked!==t&&cr[e].click()}function We(e){let t=cr[e];return d(t,"change").pipe(m(()=>t.checked),q(t.checked))}function ua(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function da(){return T(d(window,"compositionstart").pipe(m(()=>!0)),d(window,"compositionend").pipe(m(()=>!1))).pipe(q(!1))}function Go(){let e=d(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:Bo("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Re();if(typeof o!="undefined")return!ua(o,r)}return!0}),le());return da().pipe(b(t=>t?L:e))}function ve(){return new URL(location.href)}function st(e,t=!1){if(G("navigation.instant")&&!t){let r=E("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function Jo(){return new v}function Xo(){return location.hash.slice(1)}function Zo(e){let t=E("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function ha(e){return T(d(window,"hashchange"),e).pipe(m(Xo),q(Xo()),g(t=>t.length>0),B(1))}function en(e){return ha(e).pipe(m(t=>me(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function At(e){let t=matchMedia(e);return nr(r=>t.addListener(()=>r(t.matches))).pipe(q(t.matches))}function tn(){let e=matchMedia("print");return T(d(window,"beforeprint").pipe(m(()=>!0)),d(window,"afterprint").pipe(m(()=>!1))).pipe(q(e.matches))}function Ur(e,t){return e.pipe(b(r=>r?t():L))}function Wr(e,t){return new j(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function De(e,t){return Wr(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),B(1))}function rn(e,t){let r=new DOMParser;return Wr(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),B(1))}function on(e,t){let r=new DOMParser;return Wr(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),B(1))}function nn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function an(){return T(d(window,"scroll",{passive:!0}),d(window,"resize",{passive:!0})).pipe(m(nn),q(nn()))}function sn(){return{width:innerWidth,height:innerHeight}}function cn(){return d(window,"resize",{passive:!0}).pipe(m(sn),q(sn()))}function pn(){return Q([an(),cn()]).pipe(m(([e,t])=>({offset:e,size:t})),B(1))}function pr(e,{viewport$:t,header$:r}){let o=t.pipe(X("size")),n=Q([o,r]).pipe(m(()=>Ue(e)));return Q([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function ba(e){return d(e,"message",t=>t.data)}function va(e){let t=new v;return t.subscribe(r=>e.postMessage(r)),t}function ln(e,t=new Worker(e)){let r=ba(t),o=va(t),n=new v;n.subscribe(o);let i=o.pipe(ee(),oe(!0));return n.pipe(ee(),$e(r.pipe(U(i))),le())}var ga=P("#__config"),Et=JSON.parse(ga.textContent);Et.base=`${new URL(Et.base,ve())}`;function we(){return Et}function G(e){return Et.features.includes(e)}function ge(e,t){return typeof t!="undefined"?Et.translations[e].replace("#",t.toString()):Et.translations[e]}function Te(e,t=document){return P(`[data-md-component=${e}]`,t)}function ne(e,t=document){return R(`[data-md-component=${e}]`,t)}function xa(e){let t=P(".md-typeset > :first-child",e);return d(t,"click",{once:!0}).pipe(m(()=>P(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function mn(e){if(!G("announce.dismiss")||!e.childElementCount)return L;if(!e.hidden){let t=P(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new v;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),xa(e).pipe(y(r=>t.next(r)),_(()=>t.complete()),m(r=>F({ref:e},r)))})}function ya(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function fn(e,t){let r=new v;return r.subscribe(({hidden:o})=>{e.hidden=o}),ya(e,t).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))}function Ct(e,t){return t==="inline"?E("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},E("div",{class:"md-tooltip__inner md-typeset"})):E("div",{class:"md-tooltip",id:e,role:"tooltip"},E("div",{class:"md-tooltip__inner md-typeset"}))}function un(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return E("aside",{class:"md-annotation",tabIndex:0},Ct(t),E("a",{href:r,class:"md-annotation__index",tabIndex:-1},E("span",{"data-md-annotation-id":e})))}else return E("aside",{class:"md-annotation",tabIndex:0},Ct(t),E("span",{class:"md-annotation__index",tabIndex:-1},E("span",{"data-md-annotation-id":e})))}function dn(e){return E("button",{class:"md-clipboard md-icon",title:ge("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function Dr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,E("del",null,p)," "],[]).slice(0,-1),i=we(),s=new URL(e.location,i.base);G("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=we();return E("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},E("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&E("div",{class:"md-search-result__icon md-icon"}),r>0&&E("h1",null,e.title),r<=0&&E("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return E("span",{class:`md-tag ${p}`},c)}),o>0&&n.length>0&&E("p",{class:"md-search-result__terms"},ge("search.result.term.missing"),": ",...n)))}function hn(e){let t=e[0].score,r=[...e],o=we(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreDr(l,1)),...c.length?[E("details",{class:"md-search-result__more"},E("summary",{tabIndex:-1},E("div",null,c.length>0&&c.length===1?ge("search.result.more.one"):ge("search.result.more.other",c.length))),...c.map(l=>Dr(l,1)))]:[]];return E("li",{class:"md-search-result__item"},p)}function bn(e){return E("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>E("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?ar(r):r)))}function Nr(e){let t=`tabbed-control tabbed-control--${e}`;return E("div",{class:t,hidden:!0},E("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function vn(e){return E("div",{class:"md-typeset__scrollwrap"},E("div",{class:"md-typeset__table"},e))}function Ea(e){let t=we(),r=new URL(`../${e.version}/`,t.base);return E("li",{class:"md-version__item"},E("a",{href:`${r}`,class:"md-version__link"},e.title))}function gn(e,t){return E("div",{class:"md-version"},E("button",{class:"md-version__current","aria-label":ge("select.version")},t.title),E("ul",{class:"md-version__list"},e.map(Ea)))}var wa=0;function Ta(e,t){document.body.append(e);let{width:r}=pe(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=sr(t),n=typeof o!="undefined"?et(o):$({x:0,y:0}),i=T(vt(t),Vo(t)).pipe(Y());return Q([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Ue(t),l=pe(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function Ge(e){let t=e.title;if(!t.length)return L;let r=`__tooltip_${wa++}`,o=Ct(r,"inline"),n=P(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new v;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),T(i.pipe(g(({active:s})=>s)),i.pipe(be(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Me(16,de)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(_t(125,de),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Ta(o,e).pipe(y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))}).pipe(ze(ie))}function Sa(e,t){let r=H(()=>Q([zo(e),et(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=pe(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return vt(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),ye(+!o||1/0))))}function xn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new v,s=i.pipe(ee(),oe(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),yt(e).pipe(U(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),T(i.pipe(g(({active:a})=>a)),i.pipe(be(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Me(16,de)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(_t(125,de),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),d(n,"click").pipe(U(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),d(n,"mousedown").pipe(U(s),ae(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Re())==null||p.blur()}}),r.pipe(U(s),g(a=>a===o),Ye(125)).subscribe(()=>e.focus()),Sa(e,t).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))})}function Oa(e){return e.tagName==="CODE"?R(".c, .c1, .cm",e):[e]}function Ma(e){let t=[];for(let r of Oa(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function yn(e,t){t.append(...Array.from(e.childNodes))}function lr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Ma(t)){let[,c]=a.textContent.match(/\((\d+)\)/);me(`:scope > li:nth-child(${c})`,e)&&(s.set(c,un(c,i)),a.replaceWith(s.get(c)))}return s.size===0?L:H(()=>{let a=new v,c=a.pipe(ee(),oe(!0)),p=[];for(let[l,f]of s)p.push([P(".md-typeset",f),P(`:scope > li:nth-child(${l})`,e)]);return o.pipe(U(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?yn(f,u):yn(u,f)}),T(...[...s].map(([,l])=>xn(l,t,{target$:r}))).pipe(_(()=>a.complete()),le())})}function En(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return En(t)}}function wn(e,t){return H(()=>{let r=En(e);return typeof r!="undefined"?lr(r,e,t):L})}var Tn=jt(zr());var La=0;function Sn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Sn(t)}}function _a(e){return Ee(e).pipe(m(({width:t})=>({scrollable:xt(e).width>t})),X("scrollable"))}function On(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new v,i=n.pipe($r(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[];if(Tn.default.isSupported()&&(e.closest(".copy")||G("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${La++}`;let p=dn(c.id);c.insertBefore(p,e),G("content.tooltips")&&s.push(Ge(p))}let a=e.closest(".highlight");if(a instanceof HTMLElement){let c=Sn(a);if(typeof c!="undefined"&&(a.classList.contains("annotate")||G("content.code.annotate"))){let p=lr(c,e,t);s.push(Ee(a).pipe(U(i),m(({width:l,height:f})=>l&&f),Y(),b(l=>l?p:L)))}}return _a(e).pipe(y(c=>n.next(c)),_(()=>n.complete()),m(c=>F({ref:e},c)),$e(...s))});return G("content.lazy")?yt(e).pipe(g(n=>n),ye(1),b(()=>o)):o}function Aa(e,{target$:t,print$:r}){let o=!0;return T(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),y(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Mn(e,t){return H(()=>{let r=new v;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Aa(e,t).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}var Ln=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var qr,ka=0;function Ha(){return typeof mermaid=="undefined"||mermaid instanceof Element?gt("https://unpkg.com/mermaid@10.7.0/dist/mermaid.min.js"):$(void 0)}function _n(e){return e.classList.remove("mermaid"),qr||(qr=Ha().pipe(y(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Ln,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),B(1))),qr.subscribe(()=>ro(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${ka++}`,r=E("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),qr.pipe(m(()=>({ref:e})))}var An=E("table");function Cn(e){return e.replaceWith(An),An.replaceWith(vn(e)),$({ref:e})}function $a(e){let t=e.find(r=>r.checked)||e[0];return T(...e.map(r=>d(r,"change").pipe(m(()=>P(`label[for="${r.id}"]`))))).pipe(q(P(`label[for="${t.id}"]`)),m(r=>({active:r})))}function kn(e,{viewport$:t,target$:r}){let o=P(".tabbed-labels",e),n=R(":scope > input",e),i=Nr("prev");e.append(i);let s=Nr("next");return e.append(s),H(()=>{let a=new v,c=a.pipe(ee(),oe(!0));Q([a,Ee(e)]).pipe(U(c),Me(1,de)).subscribe({next([{active:p},l]){let f=Ue(p),{width:u}=pe(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let h=ir(o);(f.xh.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),Q([et(o),Ee(o)]).pipe(U(c)).subscribe(([p,l])=>{let f=xt(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),T(d(i,"click").pipe(m(()=>-1)),d(s,"click").pipe(m(()=>1))).pipe(U(c)).subscribe(p=>{let{width:l}=pe(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(U(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=P(`label[for="${p.id}"]`);l.replaceChildren(E("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),d(l.firstElementChild,"click").pipe(U(c),g(f=>!(f.metaKey||f.ctrlKey)),y(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return G("content.tabs.link")&&a.pipe(Le(1),ae(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let w of R("[data-tabs]"))for(let A of R(":scope > input",w)){let Z=P(`label[for="${A.id}"]`);if(Z!==p&&Z.innerText.trim()===f){Z.setAttribute("data-md-switching",""),A.click();break}}window.scrollTo({top:e.offsetTop-u});let h=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...h])])}}),a.pipe(U(c)).subscribe(()=>{for(let p of R("audio, video",e))p.pause()}),$a(n).pipe(y(p=>a.next(p)),_(()=>a.complete()),m(p=>F({ref:e},p)))}).pipe(ze(ie))}function Hn(e,{viewport$:t,target$:r,print$:o}){return T(...R(".annotate:not(.highlight)",e).map(n=>wn(n,{target$:r,print$:o})),...R("pre:not(.mermaid) > code",e).map(n=>On(n,{target$:r,print$:o})),...R("pre.mermaid",e).map(n=>_n(n)),...R("table:not([class])",e).map(n=>Cn(n)),...R("details",e).map(n=>Mn(n,{target$:r,print$:o})),...R("[data-tabs]",e).map(n=>kn(n,{viewport$:t,target$:r})),...R("[title]",e).filter(()=>G("content.tooltips")).map(n=>Ge(n)))}function Ra(e,{alert$:t}){return t.pipe(b(r=>T($(!0),$(!1).pipe(Ye(2e3))).pipe(m(o=>({message:r,active:o})))))}function $n(e,t){let r=P(".md-typeset",e);return H(()=>{let o=new v;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ra(e,t).pipe(y(n=>o.next(n)),_(()=>o.complete()),m(n=>F({ref:e},n)))})}function Pa({viewport$:e}){if(!G("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Ke(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=We("search");return Q([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),q(!1))}function Rn(e,t){return H(()=>Q([Ee(e),Pa(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),B(1))}function Pn(e,{header$:t,main$:r}){return H(()=>{let o=new v,n=o.pipe(ee(),oe(!0));o.pipe(X("active"),je(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(R("[title]",e)).pipe(g(()=>G("content.tooltips")),re(s=>Ge(s)));return r.subscribe(o),t.pipe(U(n),m(s=>F({ref:e},s)),$e(i.pipe(U(n))))})}function Ia(e,{viewport$:t,header$:r}){return pr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=pe(e);return{active:o>=n}}),X("active"))}function In(e,t){return H(()=>{let r=new v;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=me(".md-content h1");return typeof o=="undefined"?L:Ia(o,t).pipe(y(n=>r.next(n)),_(()=>r.complete()),m(n=>F({ref:e},n)))})}function Fn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Ee(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),X("bottom"))));return Q([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function Fa(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(re(o=>d(o,"change").pipe(m(()=>o))),q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),B(1))}function jn(e){let t=R("input",e),r=E("meta",{name:"theme-color"});document.head.appendChild(r);let o=E("meta",{name:"color-scheme"});document.head.appendChild(o);let n=At("(prefers-color-scheme: light)");return H(()=>{let i=new v;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;a{let s=Te("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(Oe(ie)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),Fa(t).pipe(U(n.pipe(Le(1))),at(),y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))})}function Un(e,{progress$:t}){return H(()=>{let r=new v;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(y(o=>r.next({value:o})),_(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Kr=jt(zr());function ja(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Wn({alert$:e}){Kr.default.isSupported()&&new j(t=>{new Kr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||ja(P(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(y(t=>{t.trigger.focus()}),m(()=>ge("clipboard.copied"))).subscribe(e)}function Dn(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function Ua(e,t){let r=new Map;for(let o of R("url",e)){let n=P("loc",o),i=[Dn(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of R("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(Dn(new URL(a),t))}}return r}function mr(e){return on(new URL("sitemap.xml",e)).pipe(m(t=>Ua(t,new URL(e))),he(()=>$(new Map)))}function Wa(e,t){if(!(e.target instanceof Element))return L;let r=e.target.closest("a");if(r===null)return L;if(r.target||e.metaKey||e.ctrlKey)return L;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(new URL(r.href))):L}function Nn(e){let t=new Map;for(let r of R(":scope > *",e.head))t.set(r.outerHTML,r);return t}function Vn(e){for(let t of R("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function Da(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...G("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=me(o),i=me(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=Nn(document);for(let[o,n]of Nn(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Te("container");return Fe(R("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new j(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),L}),ee(),oe(document))}function zn({location$:e,viewport$:t,progress$:r}){let o=we();if(location.protocol==="file:")return L;let n=mr(o.base);$(document).subscribe(Vn);let i=d(document.body,"click").pipe(je(n),b(([c,p])=>Wa(c,p)),le()),s=d(window,"popstate").pipe(m(ve),le());i.pipe(ae(t)).subscribe(([c,{offset:p}])=>{history.replaceState(p,""),history.pushState(null,"",c)}),T(i,s).subscribe(e);let a=e.pipe(X("pathname"),b(c=>rn(c,{progress$:r}).pipe(he(()=>(st(c,!0),L)))),b(Vn),b(Da),le());return T(a.pipe(ae(e,(c,p)=>p)),e.pipe(X("pathname"),b(()=>e),X("hash")),e.pipe(Y((c,p)=>c.pathname===p.pathname&&c.hash===p.hash),b(()=>i),y(()=>history.back()))).subscribe(c=>{var p,l;history.state!==null||!c.hash?window.scrollTo(0,(l=(p=history.state)==null?void 0:p.y)!=null?l:0):(history.scrollRestoration="auto",Zo(c.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),d(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(X("offset"),be(100)).subscribe(({offset:c})=>{history.replaceState(c,"")}),a}var Qn=jt(Kn());function Yn(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,Qn.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function Ht(e){return e.type===1}function fr(e){return e.type===3}function Bn(e,t){let r=ln(e);return T($(location.protocol!=="file:"),We("search")).pipe(He(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:G("search.suggest")}}})),r}function Gn({document$:e}){let t=we(),r=De(new URL("../versions.json",t.base)).pipe(he(()=>L)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>d(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),ae(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?L:(i.preventDefault(),$(c))}}return L}),b(i=>{let{version:s}=n.get(i);return mr(new URL(i)).pipe(m(a=>{let p=ve().href.replace(t.base,"");return a.has(p.split("#")[0])?new URL(`../${s}/${p}`,t.base):new URL(i)}))})))).subscribe(n=>st(n,!0)),Q([r,o]).subscribe(([n,i])=>{P(".md-header__topic").appendChild(gn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var s;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let a=((s=t.version)==null?void 0:s.default)||"latest";Array.isArray(a)||(a=[a]);e:for(let c of a)for(let p of n.aliases.concat(n.version))if(new RegExp(c,"i").test(p)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let a of ne("outdated"))a.hidden=!1})}function Ka(e,{worker$:t}){let{searchParams:r}=ve();r.has("q")&&(Be("search",!0),e.value=r.get("q"),e.focus(),We("search").pipe(He(i=>!i)).subscribe(()=>{let i=ve();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=vt(e),n=T(t.pipe(He(Ht)),d(e,"keyup"),o).pipe(m(()=>e.value),Y());return Q([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),B(1))}function Jn(e,{worker$:t}){let r=new v,o=r.pipe(ee(),oe(!0));Q([t.pipe(He(Ht)),r],(i,s)=>s).pipe(X("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(X("focus")).subscribe(({focus:i})=>{i&&Be("search",i)}),d(e.form,"reset").pipe(U(o)).subscribe(()=>e.focus());let n=P("header [for=__search]");return d(n,"click").subscribe(()=>e.focus()),Ka(e,{worker$:t}).pipe(y(i=>r.next(i)),_(()=>r.complete()),m(i=>F({ref:e},i)),B(1))}function Xn(e,{worker$:t,query$:r}){let o=new v,n=Yo(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=P(":scope > :first-child",e),a=P(":scope > :last-child",e);We("search").subscribe(l=>a.setAttribute("role",l?"list":"presentation")),o.pipe(ae(r),Ir(t.pipe(He(Ht)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?ge("search.result.none"):ge("search.result.placeholder");break;case 1:s.textContent=ge("search.result.one");break;default:let u=ar(l.length);s.textContent=ge("search.result.other",u)}});let c=o.pipe(y(()=>a.innerHTML=""),b(({items:l})=>T($(...l.slice(0,10)),$(...l.slice(10)).pipe(Ke(4),jr(n),b(([f])=>f)))),m(hn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(re(l=>{let f=me("details",l);return typeof f=="undefined"?L:d(f,"toggle").pipe(U(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(fr),m(({data:l})=>l)).pipe(y(l=>o.next(l)),_(()=>o.complete()),m(l=>F({ref:e},l)))}function Qa(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=ve();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Zn(e,t){let r=new v,o=r.pipe(ee(),oe(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),d(e,"click").pipe(U(o)).subscribe(n=>n.preventDefault()),Qa(e,t).pipe(y(n=>r.next(n)),_(()=>r.complete()),m(n=>F({ref:e},n)))}function ei(e,{worker$:t,keyboard$:r}){let o=new v,n=Te("search-query"),i=T(d(n,"keydown"),d(n,"focus")).pipe(Oe(ie),m(()=>n.value),Y());return o.pipe(je(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{switch(a.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(g(fr),m(({data:a})=>a)).pipe(y(a=>o.next(a)),_(()=>o.complete()),m(()=>({ref:e})))}function ti(e,{index$:t,keyboard$:r}){let o=we();try{let n=Bn(o.search,t),i=Te("search-query",e),s=Te("search-result",e);d(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>Be("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Re();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of R(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,h])=>h-u);f.click()}c.claim()}break;case"Escape":case"Tab":Be("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...R(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Re()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Jn(i,{worker$:n});return T(a,Xn(s,{worker$:n,query$:a})).pipe($e(...ne("search-share",e).map(c=>Zn(c,{query$:a})),...ne("search-suggest",e).map(c=>ei(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,qe}}function ri(e,{index$:t,location$:r}){return Q([t,r.pipe(q(ve()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>Yn(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=E("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ya(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return Q([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function Qr(e,o){var n=o,{header$:t}=n,r=to(n,["header$"]);let i=P(".md-sidebar__scrollwrap",e),{y:s}=Ue(i);return H(()=>{let a=new v,c=a.pipe(ee(),oe(!0)),p=a.pipe(Me(0,de));return p.pipe(ae(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(He()).subscribe(()=>{for(let l of R(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=pe(f);f.scrollTo({top:u-h/2})}}}),fe(R("label[tabindex]",e)).pipe(re(l=>d(l,"click").pipe(Oe(ie),m(()=>l),U(c)))).subscribe(l=>{let f=P(`[id="${l.htmlFor}"]`);P(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),Ya(e,r).pipe(y(l=>a.next(l)),_(()=>a.complete()),m(l=>F({ref:e},l)))})}function oi(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return Lt(De(`${r}/releases/latest`).pipe(he(()=>L),m(o=>({version:o.tag_name})),Qe({})),De(r).pipe(he(()=>L),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>F(F({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return De(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ni(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return De(r).pipe(he(()=>L),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))}function ii(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return oi(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ni(r,o)}return L}var Ba;function Ga(e){return Ba||(Ba=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(ne("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return L}return ii(e.href).pipe(y(o=>__md_set("__source",o,sessionStorage)))}).pipe(he(()=>L),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),B(1)))}function ai(e){let t=P(":scope > :last-child",e);return H(()=>{let r=new v;return r.subscribe(({facts:o})=>{t.appendChild(bn(o)),t.classList.add("md-source__repository--active")}),Ga(e).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}function Ja(e,{viewport$:t,header$:r}){return Ee(document.body).pipe(b(()=>pr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),X("hidden"))}function si(e,t){return H(()=>{let r=new v;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(G("navigation.tabs.sticky")?$({hidden:!1}):Ja(e,t)).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}function Xa(e,{viewport$:t,header$:r}){let o=new Map,n=R(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=me(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(X("height"),m(({height:a})=>{let c=Te("main"),p=P(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Ee(document.body).pipe(X("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let h=f.offsetParent;for(;h;h=h.offsetParent)u+=h.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),je(i),b(([c,p])=>t.pipe(Rr(([l,f],{offset:{y:u},size:h})=>{let w=u+h.height>=Math.floor(a.height);for(;f.length;){let[,A]=f[0];if(A-p=u&&!w)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),q({prev:[],next:[]}),Ke(2,1),m(([a,c])=>a.prev.length{let i=new v,s=i.pipe(ee(),oe(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),G("toc.follow")){let a=T(t.pipe(be(1),m(()=>{})),t.pipe(be(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),je(o.pipe(Oe(ie))),ae(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=sr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=pe(f);f.scrollTo({top:u-h/2,behavior:p})}}})}return G("navigation.tracking")&&t.pipe(U(s),X("offset"),be(250),Le(1),U(n.pipe(Le(1))),at({delay:250}),ae(i)).subscribe(([,{prev:a}])=>{let c=ve(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),Xa(e,{viewport$:t,header$:r}).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))})}function Za(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),Ke(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return Q([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),U(o.pipe(Le(1))),oe(!0),at({delay:250}),m(s=>({hidden:s})))}function pi(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new v,s=i.pipe(ee(),oe(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(U(s),X("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),d(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),Za(e,{viewport$:t,main$:o,target$:n}).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))}function li({document$:e}){e.pipe(b(()=>R(".md-ellipsis")),re(t=>yt(t).pipe(U(e.pipe(Le(1))),g(r=>r),m(()=>t),ye(1))),g(t=>t.offsetWidth{let r=t.innerText,o=t.closest("a")||t;return o.title=r,Ge(o).pipe(U(e.pipe(Le(1))),_(()=>o.removeAttribute("title")))})).subscribe(),e.pipe(b(()=>R(".md-status")),re(t=>Ge(t))).subscribe()}function mi({document$:e,tablet$:t}){e.pipe(b(()=>R(".md-toggle--indeterminate")),y(r=>{r.indeterminate=!0,r.checked=!1}),re(r=>d(r,"change").pipe(Fr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),ae(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function es(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function fi({document$:e}){e.pipe(b(()=>R("[data-md-scrollfix]")),y(t=>t.removeAttribute("data-md-scrollfix")),g(es),re(t=>d(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function ui({viewport$:e,tablet$:t}){Q([We("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(Ye(r?400:100))),ae(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ts(){return location.protocol==="file:"?gt(`${new URL("search/search_index.js",Yr.base)}`).pipe(m(()=>__index),B(1)):De(new URL("search/search_index.json",Yr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var rt=No(),Rt=Jo(),wt=en(Rt),Br=Go(),_e=pn(),ur=At("(min-width: 960px)"),hi=At("(min-width: 1220px)"),bi=tn(),Yr=we(),vi=document.forms.namedItem("search")?ts():qe,Gr=new v;Wn({alert$:Gr});var Jr=new v;G("navigation.instant")&&zn({location$:Rt,viewport$:_e,progress$:Jr}).subscribe(rt);var di;((di=Yr.version)==null?void 0:di.provider)==="mike"&&Gn({document$:rt});T(Rt,wt).pipe(Ye(125)).subscribe(()=>{Be("drawer",!1),Be("search",!1)});Br.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=me("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=me("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Re();o instanceof HTMLLabelElement&&o.click()}});li({document$:rt});mi({document$:rt,tablet$:ur});fi({document$:rt});ui({viewport$:_e,tablet$:ur});var tt=Rn(Te("header"),{viewport$:_e}),$t=rt.pipe(m(()=>Te("main")),b(e=>Fn(e,{viewport$:_e,header$:tt})),B(1)),rs=T(...ne("consent").map(e=>fn(e,{target$:wt})),...ne("dialog").map(e=>$n(e,{alert$:Gr})),...ne("header").map(e=>Pn(e,{viewport$:_e,header$:tt,main$:$t})),...ne("palette").map(e=>jn(e)),...ne("progress").map(e=>Un(e,{progress$:Jr})),...ne("search").map(e=>ti(e,{index$:vi,keyboard$:Br})),...ne("source").map(e=>ai(e))),os=H(()=>T(...ne("announce").map(e=>mn(e)),...ne("content").map(e=>Hn(e,{viewport$:_e,target$:wt,print$:bi})),...ne("content").map(e=>G("search.highlight")?ri(e,{index$:vi,location$:Rt}):L),...ne("header-title").map(e=>In(e,{viewport$:_e,header$:tt})),...ne("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Ur(hi,()=>Qr(e,{viewport$:_e,header$:tt,main$:$t})):Ur(ur,()=>Qr(e,{viewport$:_e,header$:tt,main$:$t}))),...ne("tabs").map(e=>si(e,{viewport$:_e,header$:tt})),...ne("toc").map(e=>ci(e,{viewport$:_e,header$:tt,main$:$t,target$:wt})),...ne("top").map(e=>pi(e,{viewport$:_e,header$:tt,main$:$t,target$:wt})))),gi=rt.pipe(b(()=>os),$e(rs),B(1));gi.subscribe();window.document$=rt;window.location$=Rt;window.target$=wt;window.keyboard$=Br;window.viewport$=_e;window.tablet$=ur;window.screen$=hi;window.print$=bi;window.alert$=Gr;window.progress$=Jr;window.component$=gi;})(); +//# sourceMappingURL=bundle.c8d2eff1.min.js.map + diff --git a/assets/javascripts/bundle.c8d2eff1.min.js.map b/assets/javascripts/bundle.c8d2eff1.min.js.map new file mode 100644 index 00000000..fc522dba --- /dev/null +++ b/assets/javascripts/bundle.c8d2eff1.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/tslib.es6.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an + +

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

+ + +
+

The Most Useful Book for a Go Programmer?

+ + +

How to make mistakes in Go - Go Time #190

+ + + +

Go is AMAZING

+ + +

8LU - 100% Test Coverage

+ + +

Some Tips I learned from 100 Mistakes in Go

+

Post

+

What can be summarized from 100 Go Mistakes?

+

Post

+

Book review: 100 Go Mistakes and How to Avoid Them

+

Post

+

Chinese

+

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

+

Post

+

100 Go Mistakes 随记

+

Post

+

我为什么放弃Go语言?

+

Post

+

Japanese

+

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

+

Post

+

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

+

Post

+

Portuguese

+

Um ÓTIMO livro para programadores Go

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/img/20-slice.png b/img/20-slice.png new file mode 100644 index 00000000..a119936e Binary files /dev/null and b/img/20-slice.png differ diff --git a/img/28-maps-memory-leaks.png b/img/28-maps-memory-leaks.png new file mode 100644 index 00000000..88d702a0 Binary files /dev/null and b/img/28-maps-memory-leaks.png differ diff --git a/img/56-concurrency-faster.png b/img/56-concurrency-faster.png new file mode 100644 index 00000000..a3b8c27d Binary files /dev/null and b/img/56-concurrency-faster.png differ diff --git a/img/89-benchmarks.png b/img/89-benchmarks.png new file mode 100644 index 00000000..3415bc1f Binary files /dev/null and b/img/89-benchmarks.png differ diff --git a/img/98-profiling-execution-tracing.png b/img/98-profiling-execution-tracing.png new file mode 100644 index 00000000..bb39fa6c Binary files /dev/null and b/img/98-profiling-execution-tracing.png differ diff --git a/img/Go-Logo_Aqua.svg b/img/Go-Logo_Aqua.svg new file mode 100644 index 00000000..bccf226b --- /dev/null +++ b/img/Go-Logo_Aqua.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/Go-Logo_Black.svg b/img/Go-Logo_Black.svg new file mode 100644 index 00000000..042cbbaa --- /dev/null +++ b/img/Go-Logo_Black.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/img/Go-Logo_Blue.svg b/img/Go-Logo_Blue.svg new file mode 100644 index 00000000..64e96207 --- /dev/null +++ b/img/Go-Logo_Blue.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/Go-Logo_Fuchsia.svg b/img/Go-Logo_Fuchsia.svg new file mode 100644 index 00000000..9efd1590 --- /dev/null +++ b/img/Go-Logo_Fuchsia.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/img/Go-Logo_LightBlue.svg b/img/Go-Logo_LightBlue.svg new file mode 100644 index 00000000..55a50717 --- /dev/null +++ b/img/Go-Logo_LightBlue.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/Go-Logo_White.svg b/img/Go-Logo_White.svg new file mode 100644 index 00000000..4a0a5821 --- /dev/null +++ b/img/Go-Logo_White.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/Go-Logo_Yellow.svg b/img/Go-Logo_Yellow.svg new file mode 100644 index 00000000..7733929b --- /dev/null +++ b/img/Go-Logo_Yellow.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/cover-cn.jpg b/img/cover-cn.jpg new file mode 100644 index 00000000..4527551b Binary files /dev/null and b/img/cover-cn.jpg differ diff --git a/img/cover-en.jpg b/img/cover-en.jpg new file mode 100644 index 00000000..4f892e50 Binary files /dev/null and b/img/cover-en.jpg differ diff --git a/img/cover-jp.jpg b/img/cover-jp.jpg new file mode 100644 index 00000000..dff95476 Binary files /dev/null and b/img/cover-jp.jpg differ diff --git a/img/cover-kr.png b/img/cover-kr.png new file mode 100644 index 00000000..3d2d1e88 Binary files /dev/null and b/img/cover-kr.png differ diff --git a/img/cover.png b/img/cover.png new file mode 100644 index 00000000..33422b70 Binary files /dev/null and b/img/cover.png differ diff --git a/img/false-sharing-1.svg b/img/false-sharing-1.svg new file mode 100644 index 00000000..c6c1a4fb --- /dev/null +++ b/img/false-sharing-1.svg @@ -0,0 +1,3 @@ + + +
Main memory
Main memory
sumA
sumA
sumB
sumB
...
...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/img/false-sharing-2.svg b/img/false-sharing-2.svg new file mode 100644 index 00000000..0e48a5be --- /dev/null +++ b/img/false-sharing-2.svg @@ -0,0 +1,3 @@ + + +
Core 0
Core 0
Main memory
Main memory
sumA
sumA
sumB
sumB
...
...
L1D
L1D
sumA
sumA
sumB
sumB
...
...
Core 1
Core 1
L1D
L1D
sumA
sumA
sumB
sumB
...
...
Fetch cache line
Fetch...
Fetch cache line
Fetch...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/img/false-sharing-3.svg b/img/false-sharing-3.svg new file mode 100644 index 00000000..b8b3bc36 --- /dev/null +++ b/img/false-sharing-3.svg @@ -0,0 +1,3 @@ + + +
Core 0
Core 0
L1D
L1D
sumA
sumA
sumB
sumB
...
...
Core 1
Core 1
L1D
L1D
sumA
sumA
sumB
sumB
...
...
Goroutine 2
Goroutine 2
Goroutine 1
Goroutine 1
Update
Update
Update
Update
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/img/false-sharing-4.svg b/img/false-sharing-4.svg new file mode 100644 index 00000000..4ec79056 --- /dev/null +++ b/img/false-sharing-4.svg @@ -0,0 +1,3 @@ + + +
Core 0
Core 0
Main memory
Main memory
sumA
sumA
...
...
L1D
L1D
sumA
sumA
...
...
Core 1
Core 1
L1D
L1D
...
...
sumB
sumB
56 bytes of padding
56 by...
sumB
sumB
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/img/false-sharing.jpeg b/img/false-sharing.jpeg new file mode 100644 index 00000000..3c36eff0 Binary files /dev/null and b/img/false-sharing.jpeg differ diff --git a/img/go-scheduler.png b/img/go-scheduler.png new file mode 100644 index 00000000..6a372dbb Binary files /dev/null and b/img/go-scheduler.png differ diff --git a/img/gopher.png b/img/gopher.png new file mode 100644 index 00000000..0e04cf57 Binary files /dev/null and b/img/gopher.png differ diff --git a/img/inside-cover.png b/img/inside-cover.png new file mode 100644 index 00000000..9c112057 Binary files /dev/null and b/img/inside-cover.png differ diff --git a/img/interface-pollution.jpeg b/img/interface-pollution.jpeg new file mode 100644 index 00000000..39dd5fc7 Binary files /dev/null and b/img/interface-pollution.jpeg differ diff --git a/img/ioreaderwriter.svg b/img/ioreaderwriter.svg new file mode 100644 index 00000000..3fa80417 --- /dev/null +++ b/img/ioreaderwriter.svg @@ -0,0 +1,3 @@ + + +
Data Source
Data Source
Read
Read
Target
Target
Write
Write
io.Reader and io.Writer Interfaces
io.Reader and io.Writer Interfaces
io.Reader
io.Reader
io.Writer
io.Writer
H
H
E
E
L
L
L
L
O
O
byte slice
byte slice
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/img/map-leak-1.png b/img/map-leak-1.png new file mode 100644 index 00000000..7c478152 Binary files /dev/null and b/img/map-leak-1.png differ diff --git a/img/map-leak-2.png b/img/map-leak-2.png new file mode 100644 index 00000000..38aca8c3 Binary files /dev/null and b/img/map-leak-2.png differ diff --git a/img/matrix.png b/img/matrix.png new file mode 100644 index 00000000..d7296deb Binary files /dev/null and b/img/matrix.png differ diff --git a/img/mergesort.png b/img/mergesort.png new file mode 100644 index 00000000..60d206db Binary files /dev/null and b/img/mergesort.png differ diff --git a/img/options.png b/img/options.png new file mode 100644 index 00000000..16d32695 Binary files /dev/null and b/img/options.png differ diff --git a/img/ratings-amazon.png b/img/ratings-amazon.png new file mode 100644 index 00000000..b3063b6a Binary files /dev/null and b/img/ratings-amazon.png differ diff --git a/img/ratings-goodreads.png b/img/ratings-goodreads.png new file mode 100644 index 00000000..9de92da0 Binary files /dev/null and b/img/ratings-goodreads.png differ diff --git a/img/ratings-manning.png b/img/ratings-manning.png new file mode 100644 index 00000000..89151b1f Binary files /dev/null and b/img/ratings-manning.png differ diff --git a/img/rune.png b/img/rune.png new file mode 100644 index 00000000..9defe714 Binary files /dev/null and b/img/rune.png differ diff --git a/img/screen-mergesort1.png b/img/screen-mergesort1.png new file mode 100644 index 00000000..22a6d1a6 Binary files /dev/null and b/img/screen-mergesort1.png differ diff --git a/img/screen-mergesort11.png b/img/screen-mergesort11.png new file mode 100644 index 00000000..4184a06f Binary files /dev/null and b/img/screen-mergesort11.png differ diff --git a/img/screen-mergesort2.png b/img/screen-mergesort2.png new file mode 100644 index 00000000..daf82683 Binary files /dev/null and b/img/screen-mergesort2.png differ diff --git a/img/screen-pprof-cpu.png b/img/screen-pprof-cpu.png new file mode 100644 index 00000000..329fdbf1 Binary files /dev/null and b/img/screen-pprof-cpu.png differ diff --git a/img/screen-pprof-goroutines.png b/img/screen-pprof-goroutines.png new file mode 100644 index 00000000..2ce80b0f Binary files /dev/null and b/img/screen-pprof-goroutines.png differ diff --git a/img/screen-pprof-heap-diff.png b/img/screen-pprof-heap-diff.png new file mode 100644 index 00000000..4da34528 Binary files /dev/null and b/img/screen-pprof-heap-diff.png differ diff --git a/img/screen-pprof-heap.png b/img/screen-pprof-heap.png new file mode 100644 index 00000000..e3f4b2ba Binary files /dev/null and b/img/screen-pprof-heap.png differ diff --git a/img/screen-pprof-sarama.png b/img/screen-pprof-sarama.png new file mode 100644 index 00000000..cfc464cb Binary files /dev/null and b/img/screen-pprof-sarama.png differ diff --git a/img/screen-tracing-user-level.png b/img/screen-tracing-user-level.png new file mode 100644 index 00000000..52bc0523 Binary files /dev/null and b/img/screen-tracing-user-level.png differ diff --git a/img/slice-1.png b/img/slice-1.png new file mode 100644 index 00000000..a481b785 Binary files /dev/null and b/img/slice-1.png differ diff --git a/img/slice-2.png b/img/slice-2.png new file mode 100644 index 00000000..2e6905c3 Binary files /dev/null and b/img/slice-2.png differ diff --git a/img/slice-3.png b/img/slice-3.png new file mode 100644 index 00000000..8120a8a5 Binary files /dev/null and b/img/slice-3.png differ diff --git a/img/slice-4.png b/img/slice-4.png new file mode 100644 index 00000000..580383cc Binary files /dev/null and b/img/slice-4.png differ diff --git a/img/slice-5.png b/img/slice-5.png new file mode 100644 index 00000000..d89e0cd2 Binary files /dev/null and b/img/slice-5.png differ diff --git a/img/slice-6.png b/img/slice-6.png new file mode 100644 index 00000000..4d1573be Binary files /dev/null and b/img/slice-6.png differ diff --git a/img/slice-7.png b/img/slice-7.png new file mode 100644 index 00000000..2a5fb6ce Binary files /dev/null and b/img/slice-7.png differ diff --git a/img/slice-8.png b/img/slice-8.png new file mode 100644 index 00000000..a4389089 Binary files /dev/null and b/img/slice-8.png differ diff --git a/img/tracing.png b/img/tracing.png new file mode 100644 index 00000000..dd5fac31 Binary files /dev/null and b/img/tracing.png differ diff --git a/img/trim.png b/img/trim.png new file mode 100644 index 00000000..0531891a Binary files /dev/null and b/img/trim.png differ diff --git a/index.html b/index.html new file mode 100644 index 00000000..d3cf4813 --- /dev/null +++ b/index.html @@ -0,0 +1,5377 @@ + + + + + + + + + + + + + + + + + + + + + + + Common Go Mistakes - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Common Go Mistakes

+

This page is a summary of the mistakes in the 100 Go Mistakes and How to Avoid Them book. Meanwhile, it's also open to the community. If you believe that a common Go mistake should be added, please create an issue.

+
+Jobs +

Is your company hiring? Sponsor this repository and let a significant audience of Go developers (~1k unique visitors per week) know about your opportunities in this section.

+
+

+
+Beta +

You're viewing a beta version enriched with significantly more content. However, this version is not yet complete, and I'm looking for volunteers to help me summarize the remaining mistakes (GitHub issue #43).

+

Progress: +

+
+

Code and Project Organization

+

Unintended variable shadowing (#1)

+
+TL;DR +

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

+
+

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

+

Source code

+

Unnecessary nested code (#2)

+
+TL;DR +

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

+
+

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

+
    +
  • When an if block returns, we should omit the else block in all cases. For example, we shouldn’t write:
  • +
+
if foo() {
+    // ...
+    return true
+} else {
+    // ...
+}
+
+

Instead, we omit the else block like this:

+
if foo() {
+    // ...
+    return true
+}
+// ...
+
+
    +
  • We can also follow this logic with a non-happy path:
  • +
+
if s != "" {
+    // ...
+} else {
+    return errors.New("empty string")
+}
+
+

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

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

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

+

Source code

+

Misusing init functions (#3)

+
+TL;DR +

When initializing variables, remember that init functions have limited error handling and make state handling and testing more complex. In most cases, initializations should be handled as specific functions.

+
+

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

+

Init functions can lead to some issues:

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

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

+

Source code

+

Overusing getters and setters (#4)

+
+TL;DR +

Forcing the use of getters and setters isn’t idiomatic in Go. Being pragmatic and finding the right balance between efficiency and blindly following certain idioms should be the way to go.

+
+

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

+

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

+

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

+

Interface pollution (#5)

+
+TL;DR +

Abstractions should be discovered, not created. To prevent unnecessary complexity, create an interface when you need it and not when you foresee needing it, or if you can at least prove the abstraction to be a valid one.

+
+

Read the full section here.

+

Source code

+

Interface on the producer side (#6)

+
+TL;DR +

Keeping interfaces on the client side avoids unnecessary abstractions.

+
+

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

+

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

+

Source code

+

Returning interfaces (#7)

+
+TL;DR +

To prevent being restricted in terms of flexibility, a function shouldn’t return interfaces but concrete implementations in most cases. Conversely, a function should accept interfaces whenever possible.

+
+

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

+

any says nothing (#8)

+
+TL;DR +

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

+
+

The any type can be helpful if there is a genuine need for accepting or returning any possible type (for instance, when it comes to marshaling or formatting). In general, we should avoid overgeneralizing the code we write at all costs. Perhaps a little bit of duplicated code might occasionally be better if it improves other aspects such as code expressiveness.

+

Source code

+

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 full section here.

+

Source code

+

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

+
+TL;DR +

Using type embedding can also help avoid boilerplate code; however, ensure that doing so doesn’t lead to visibility issues where some fields should have remained hidden.

+
+

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

+

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

+
type Foo struct {
+    Bar // Embedded field
+}
+
+type Bar struct {
+    Baz int
+}
+
+

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

+

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

+

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

+

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

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

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

+

Source code

+

Not using the functional options pattern (#11)

+
+TL;DR +

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

+
+

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

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

+
type options struct {
+  port *int
+}
+
+type Option func(options *options) error
+
+func WithPort(port int) Option {
+  return func(options *options) error {
+    if port < 0 {
+    return errors.New("port should be positive")
+  }
+  options.port = &port
+  return nil
+  }
+}
+
+func NewServer(addr string, opts ...Option) ( *http.Server, error) {
+  var options options
+  for _, opt := range opts {
+    err := opt(&options)
+    if err != nil {
+      return nil, err
+    }
+  }
+
+  // At this stage, the options struct is built and contains the config
+  // Therefore, we can implement our logic related to port configuration
+  var port int
+  if options.port == nil {
+    port = defaultHTTPPort
+  } else {
+      if *options.port == 0 {
+      port = randomPort()
+    } else {
+      port = *options.port
+    }
+  }
+
+  // ...
+}
+
+

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

+

Source code

+

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

+

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

+

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

+

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

+

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

+

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

+
+Note +

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

+
+

Creating utility packages (#13)

+
+TL;DR +

Naming is a critical piece of application design. Creating packages such as common, util, and shared doesn’t bring much value for the reader. Refactor such packages into meaningful and specific package names.

+
+

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

+

Source code

+

Ignoring package name collisions (#14)

+
+TL;DR +

To avoid naming collisions between variables and packages, leading to confusion or perhaps even bugs, use unique names for each one. If this isn’t feasible, use an import alias to change the qualifier to differentiate the package name from the variable name, or think of a better name.

+
+

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

+

Missing code documentation (#15)

+
+TL;DR +

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

+
+

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

+

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

+

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

+

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

+

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

+

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

+

Not using linters (#16)

+
+TL;DR +

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

+
+

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

+

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

+ +

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

+ +

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

+

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

+

Data Types

+

Creating confusion with octal literals (#17)

+
+TL;DR +

When reading existing code, bear in mind that integer literals starting with 0 are octal numbers. Also, to improve readability, make octal integers explicit by prefixing them with 0o.

+
+

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

+

We should also note the other integer literal representations:

+
    +
  • Binary—Uses a 0b or 0B prefix (for example, 0b100 is equal to 4 in base 10)
  • +
  • Hexadecimal—Uses an 0x or 0X prefix (for example, 0xF is equal to 15 in base 10)
  • +
  • Imaginary—Uses an i suffix (for example, 3i)
  • +
+

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

+

Source code

+

Neglecting integer overflows (#18)

+
+TL;DR +

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

+
+

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

+
var counter int32 = math.MaxInt32 + 1
+
+
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

+

Not understanding floating-points (#19)

+
+TL;DR +

Making floating-point comparisons within a given delta can ensure that your code is portable. When performing addition or subtraction, group the operations with a similar order of magnitude to favor accuracy. Also, perform multiplication and division before addition and subtraction.

+
+

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

+

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

+
var n float32 = 1.0001
+fmt.Println(n * n)
+
+

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

+

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

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

Source code

+

Not understanding slice length and capacity (#20)

+
+TL;DR +

Understanding the difference between slice length and capacity should be part of a Go developer’s core knowledge. The slice length is the number of available elements in the slice, whereas the slice capacity is the number of elements in the backing array.

+
+

Read the full section here.

+

Source code

+

Inefficient slice initialization (#21)

+
+TL;DR +

When creating a slice, initialize it with a given length or capacity if its length is already known. This reduces the number of allocations and improves performance.

+
+

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

+

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

+

Source code

+

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

+
+TL;DR +

To prevent common confusions such as when using the encoding/json or the reflect package, you need to understand the difference between nil and empty slices. Both are zero-length, zero-capacity slices, but only a nil slice doesn’t require allocation.

+
+

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

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

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

+

Source code

+

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

+
+TL;DR +

To check if a slice doesn’t contain any element, check its length. This check works regardless of whether the slice is nil or empty. The same goes for maps. To design unambiguous APIs, you shouldn’t distinguish between nil and empty slices.

+
+

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

+

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

+

Source code

+

Not making slice copies correctly (#24)

+
+TL;DR +

To copy one slice to another using the copy built-in function, remember that the number of copied elements corresponds to the minimum between the two slice’s lengths.

+
+

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

+

Source code

+

Unexpected side effects using slice append (#25)

+
+TL;DR +

Using copy or the full slice expression is a way to prevent append from creating conflicts if two different functions use slices backed by the same array. However, only a slice copy prevents memory leaks if you want to shrink a large slice.

+
+

When using slicing, we must remember that we can face a situation leading to unintended side effects. If the resulting slice has a length smaller than its capacity, append can mutate the original slice. If we want to restrict the range of possible side effects, we can use either a slice copy or the full slice expression, which prevents us from doing a copy.

+
+Note +

s[low:high:max] (full slice expression): This statement creates a slice similar to the one created with s[low:high], except that the resulting slice’s capacity is equal to max - low.

+
+

Source code

+

Slices and memory leaks (#26)

+
+TL;DR +

Working with a slice of pointers or structs with pointer fields, you can avoid memory leaks by marking as nil the elements excluded by a slicing operation.

+
+

Leaking capacity

+

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

+

Source code

+

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

+

Inefficient map initialization (#27)

+
+TL;DR +

When creating a map, initialize it with a given length if its length is already known. This reduces the number of allocations and improves performance.

+
+

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

+

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

+

Source code

+

Maps and memory leaks (#28)

+
+TL;DR +

A map can always grow in memory, but it never shrinks. Hence, if it leads to some memory issues, you can try different options, such as forcing Go to recreate the map or using pointers.

+
+

Read the full section here.

+

Source code

+

Comparing values incorrectly (#29)

+
+TL;DR +

To compare types in Go, you can use the == and != operators if two types are comparable: Booleans, numerals, strings, pointers, channels, and structs are composed entirely of comparable types. Otherwise, you can either use reflect.DeepEqual and pay the price of reflection or use custom implementations and libraries.

+
+

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

+
    +
  • Booleans—Compare whether two Booleans are equal.
  • +
  • Numerics (int, float, and complex types)—Compare whether two numerics are equal.
  • +
  • Strings—Compare whether two strings are equal.
  • +
  • Channels—Compare whether two channels were created by the same call to make or if both are nil.
  • +
  • Interfaces—Compare whether two interfaces have identical dynamic types and equal dynamic values or if both are nil.
  • +
  • Pointers—Compare whether two pointers point to the same value in memory or if both are nil.
  • +
  • Structs and arrays—Compare whether they are composed of similar types.
  • +
+
+Note +

We can also use the ?, >=, <, and > operators with numeric types to compare values and with strings to compare their lexical order.

+
+

If operands are not comparable (e.g., slices and maps), we have to use other options such as reflection. Reflection is a form of metaprogramming, and it refers to the ability of an application to introspect and modify its structure and behavior. For example, in Go, we can use reflect.DeepEqual. This function reports whether two elements are deeply equal by recursively traversing two values. The elements it accepts are basic types plus arrays, structs, slices, maps, pointers, interfaces, and functions. Yet, the main catch is the performance penalty.

+

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

+

Source code

+

Control Structures

+

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

+
+TL;DR +

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

+
+

A range loop allows iterating over different data structures:

+
    +
  • String
  • +
  • Array
  • +
  • Pointer to an array
  • +
  • Slice
  • +
  • Map
  • +
  • Receiving channel
  • +
+

Compared to a classic for loop, a range loop is a convenient way to iterate over all the elements of one of these data structures, thanks to its concise syntax.

+

Yet, we should remember that the value element in a range loop is a copy. Therefore, if the value is a struct we need to mutate, we will only update the copy, not the element itself, unless the value or field we modify is a pointer. The favored options are to access the element via the index using a range loop or a classic for loop.

+

Source code

+

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

+
+TL;DR +

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

+
+

The range loop evaluates the provided expression only once, before the beginning of the loop, by doing a copy (regardless of the type). We should remember this behavior to avoid common mistakes that might, for example, lead us to access the wrong element. For example:

+
a := [3]int{0, 1, 2}
+for i, v := range a {
+    a[2] = 10
+    if i == 2 {
+        fmt.Println(v)
+    }
+}
+
+

This code updates the last index to 10. However, if we run this code, it does not print 10; it prints 2.

+

Source code

+

⚠ Ignoring the impacts of using pointer elements in range loops (#32)

+
+Warning +

This mistake isn't relevant anymore from Go 1.22 (details).

+
+

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

+
+TL;DR +

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

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

Source code

+

Ignoring how the break statement works (#34)

+
+TL;DR +

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

+
+

A break statement is commonly used to terminate the execution of a loop. When loops are used in conjunction with switch or select, developers frequently make the mistake of breaking the wrong statement. For example:

+
for i := 0; i < 5; i++ {
+    fmt.Printf("%d ", i)
+
+    switch i {
+    default:
+    case 2:
+        break
+    }
+}
+
+

The break statement doesn’t terminate the for loop: it terminates the switch statement, instead. Hence, instead of iterating from 0 to 2, this code iterates from 0 to 4: 0 1 2 3 4.

+

One essential rule to keep in mind is that a break statement terminates the execution of the innermost for, switch, or select statement. In the previous example, it terminates the switch statement.

+

To break the loop instead of the switch statement, the most idiomatic way is to use a label:

+
loop:
+    for i := 0; i < 5; i++ {
+        fmt.Printf("%d ", i)
+
+        switch i {
+        default:
+        case 2:
+            break loop
+        }
+    }
+
+

Here, we associate the loop label with the for loop. Then, because we provide the loop label to the break statement, it breaks the loop, not the switch. Therefore, this new version will print 0 1 2, as we expected.

+

Source code

+

Using defer inside a loop (#35)

+
+TL;DR +

Extracting loop logic inside a function leads to executing a defer statement at the end of each iteration.

+
+

The defer statement delays a call’s execution until the surrounding function returns. It’s mainly used to reduce boilerplate code. For example, if a resource has to be closed eventually, we can use defer to avoid repeating the closure calls before every single return.

+

One common mistake with defer is to forget that it schedules a function call when the surrounding function returns. For example:

+
func readFiles(ch <-chan string) error {
+    for path := range ch {
+        file, err := os.Open(path)
+        if err != nil {
+            return err
+        }
+
+        defer file.Close()
+
+        // Do something with file
+    }
+    return nil
+}
+
+

The defer calls are executed not during each loop iteration but when the readFiles function returns. If readFiles doesn’t return, the file descriptors will be kept open forever, causing leaks.

+

One common option to fix this problem is to create a surrounding function after defer, called during each iteration:

+
func readFiles(ch <-chan string) error {
+    for path := range ch {
+        if err := readFile(path); err != nil {
+            return err
+        }
+    }
+    return nil
+}
+
+func readFile(path string) error {
+    file, err := os.Open(path)
+    if err != nil {
+        return err
+    }
+
+    defer file.Close()
+
+    // Do something with file
+    return nil
+}
+
+

Another solution is to make the readFile function a closure but intrinsically, this remains the same solution: adding another surrounding function to execute the defer calls during each iteration.

+

Source code

+

Strings

+

Not understanding the concept of rune (#36)

+
+TL;DR +

Understanding that a rune corresponds to the concept of a Unicode code point and that it can be composed of multiple bytes should be part of the Go developer’s core knowledge to work accurately with strings.

+
+

As runes are everywhere in Go, it's important to understand the following:

+
    +
  • A charset is a set of characters, whereas an encoding describes how to translate a charset into binary.
  • +
  • In Go, a string references an immutable slice of arbitrary bytes.
  • +
  • Go source code is encoded using UTF-8. Hence, all string literals are UTF-8 strings. But because a string can contain arbitrary bytes, if it’s obtained from somewhere else (not the source code), it isn’t guaranteed to be based on the UTF-8 encoding.
  • +
  • A rune corresponds to the concept of a Unicode code point, meaning an item represented by a single value.
  • +
  • Using UTF-8, a Unicode code point can be encoded into 1 to 4 bytes.
  • +
  • Using len() on a string in Go returns the number of bytes, not the number of runes.
  • +
+

Source code

+

Inaccurate string iteration (#37)

+
+TL;DR +

Iterating on a string with the range operator iterates on the runes with the index corresponding to the starting index of the rune’s byte sequence. To access a specific rune index (such as the third rune), convert the string into a []rune.

+
+

Iterating on a string is a common operation for developers. Perhaps we want to perform an operation for each rune in the string or implement a custom function to search for a specific substring. In both cases, we have to iterate on the different runes of a string. But it’s easy to get confused about how iteration works.

+

For example, consider the following example:

+
s := "hêllo"
+for i := range s {
+    fmt.Printf("position %d: %c\n", i, s[i])
+}
+fmt.Printf("len=%d\n", len(s))
+
+
position 0: h
+position 1: Ã
+position 3: l
+position 4: l
+position 5: o
+len=6
+
+

Let's highlight three points that might be confusing:

+
    +
  • The second rune is à in the output instead of ê.
  • +
  • We jumped from position 1 to position 3: what is at position 2?
  • +
  • len returns a count of 6, whereas s contains only 5 runes.
  • +
+

Let’s start with the last observation. We already mentioned that len returns the number of bytes in a string, not the number of runes. Because we assigned a string literal to s, s is a UTF-8 string. Meanwhile, the special character "ê" isn’t encoded in a single byte; it requires 2 bytes. Therefore, calling len(s) returns 6.

+

Meanwhile, in the previous example, we have to understand that we don't iterate over each rune; instead, we iterate over each starting index of a rune:

+

+

Printing s[i] doesn’t print the ith rune; it prints the UTF-8 representation of the byte at index i. Hence, we printed "hÃllo" instead of "hêllo".

+

If we want to print all the different runes, we can either use the value element of the range operator:

+
s := "hêllo"
+for i, r := range s {
+    fmt.Printf("position %d: %c\n", i, r)
+}
+
+

Or, we can convert the string into a slice of runes and iterate over it:

+
s := "hêllo"
+runes := []rune(s)
+for i, r := range runes {
+    fmt.Printf("position %d: %c\n", i, r)
+}
+
+

Note that this solution introduces a run-time overhead compared to the previous one. Indeed, converting a string into a slice of runes requires allocating an additional slice and converting the bytes into runes: an O(n) time complexity with n the number of bytes in the string. Therefore, if we want to iterate over all the runes, we should use the first solution.

+

However, if we want to access the ith rune of a string with the first option, we don’t have access to the rune index; rather, we know the starting index of a rune in the byte sequence.

+
s := "hêllo"
+r := []rune(s)[4]
+fmt.Printf("%c\n", r) // o
+
+

Source code

+

Misusing trim functions (#38)

+
+TL;DR +

strings.TrimRight/strings.TrimLeft removes all the trailing/leading runes contained in a given set, whereas strings.TrimSuffix/strings.TrimPrefix returns a string without a provided suffix/prefix.

+
+

For example:

+
fmt.Println(strings.TrimRight("123oxo", "xo"))
+
+

The example prints 123:

+

+

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

+

Under-optimized strings concatenation (#39)

+
+TL;DR +

Concatenating a list of strings should be done with strings.Builder to prevent allocating a new string during each iteration.

+
+

Let’s consider a concat function that concatenates all the string elements of a slice using the += operator:

+
func concat(values []string) string {
+    s := ""
+    for _, value := range values {
+        s += value
+    }
+    return s
+}
+
+

During each iteration, the += operator concatenates s with the value string. At first sight, this function may not look wrong. But with this implementation, we forget one of the core characteristics of a string: its immutability. Therefore, each iteration doesn’t update s; it reallocates a new string in memory, which significantly impacts the performance of this function.

+

Fortunately, there is a solution to deal with this problem, using strings.Builder:

+
func concat(values []string) string {
+    sb := strings.Builder{}
+    for _, value := range values {
+        _, _ = sb.WriteString(value)
+    }
+    return sb.String()
+}
+
+

During each iteration, we constructed the resulting string by calling the WriteString method that appends the content of value to its internal buffer, hence minimizing memory copying.

+
+Note +

WriteString returns an error as the second output, but we purposely ignore it. Indeed, this method will never return a non-nil error. So what’s the purpose of this method returning an error as part of its signature? strings.Builder implements the io.StringWriter interface, which contains a single method: WriteString(s string) (n int, err error). Hence, to comply with this interface, WriteString must return an error.

+
+

Internally, strings.Builder holds a byte slice. Each call to WriteString results in a call to append on this slice. There are two impacts. First, this struct shouldn’t be used concurrently, as the calls to append would lead to race conditions. The second impact is something that we saw in mistake #21, "Inefficient slice initialization": if the future length of a slice is already known, we should preallocate it. For that purpose, strings.Builder exposes a method Grow(n int) to guarantee space for another n bytes:

+
func concat(values []string) string {
+    total := 0
+    for i := 0; i < len(values); i++ {
+        total += len(values[i])
+    }
+
+    sb := strings.Builder{}
+    sb.Grow(total) (2)
+    for _, value := range values {
+        _, _ = sb.WriteString(value)
+    }
+    return sb.String()
+}
+
+

Let’s run a benchmark to compare the three versions (v1 using +=; v2 using strings.Builder{} without preallocation; and v3 using strings.Builder{} with preallocation). The input slice contains 1,000 strings, and each string contains 1,000 bytes:

+
BenchmarkConcatV1-4             16      72291485 ns/op
+BenchmarkConcatV2-4           1188        878962 ns/op
+BenchmarkConcatV3-4           5922        190340 ns/op
+
+

As we can see, the latest version is by far the most efficient: 99% faster than v1 and 78% faster than v2.

+

strings.Builder is the recommended solution to concatenate a list of strings. Usually, this solution should be used within a loop. Indeed, if we just have to concatenate a few strings (such as a name and a surname), using strings.Builder is not recommended as doing so will make the code a bit less readable than using the += operator or fmt.Sprintf.

+

Source code

+

Useless string conversions (#40)

+
+TL;DR +

Remembering that the bytes package offers the same operations as the strings package can help avoid extra byte/string conversions.

+
+

When choosing to work with a string or a []byte, most programmers tend to favor strings for convenience. But most I/O is actually done with []byte. For example, io.Reader, io.Writer, and io.ReadAll work with []byte, not strings.

+

When we’re wondering whether we should work with strings or []byte, let’s recall that working with []byte isn’t necessarily less convenient. Indeed, all the exported functions of the strings package also have alternatives in the bytes package: Split, Count, Contains, Index, and so on. Hence, whether we’re doing I/O or not, we should first check whether we could implement a whole workflow using bytes instead of strings and avoid the price of additional conversions.

+

Source code

+

Substring and memory leaks (#41)

+
+TL;DR +

Using copies instead of substrings can prevent memory leaks, as the string returned by a substring operation will be backed by the same byte array.

+
+

In mistake #26, “Slices and memory leaks,” we saw how slicing a slice or array may lead to memory leak situations. This principle also applies to string and substring operations.

+

We need to keep two things in mind while using the substring operation in Go. First, the interval provided is based on the number of bytes, not the number of runes. Second, a substring operation may lead to a memory leak as the resulting substring will share the same backing array as the initial string. The solutions to prevent this case from happening are to perform a string copy manually or to use strings.Clone from Go 1.18.

+

Source code

+

Functions and Methods

+

Not knowing which type of receiver to use (#42)

+
+TL;DR +

The decision whether to use a value or a pointer receiver should be made based on factors such as the type, whether it has to be mutated, whether it contains a field that can’t be copied, and how large the object is. When in doubt, use a pointer receiver.

+
+

Choosing between value and pointer receivers isn’t always straightforward. Let’s discuss some of the conditions to help us choose.

+

A receiver must be a pointer

+
    +
  • If the method needs to mutate the receiver. This rule is also valid if the receiver is a slice and a method needs to append elements:
  • +
+
type slice []int
+
+func (s *slice) add(element int) {
+    *s = append(*s, element)
+}
+
+ +

A receiver should be a pointer

+
    +
  • If the receiver is a large object. Using a pointer can make the call more efficient, as doing so prevents making an extensive copy. When in doubt about how large is large, benchmarking can be the solution; it’s pretty much impossible to state a specific size, because it depends on many factors.
  • +
+

A receiver must be a value

+
    +
  • If we have to enforce a receiver’s immutability.
  • +
  • If the receiver is a map, function, or channel. Otherwise, a compilation error + occurs.
  • +
+

A receiver should be a value

+
    +
  • If the receiver is a slice that doesn’t have to be mutated.
  • +
  • If the receiver is a small array or struct that is naturally a value type without mutable fields, such as time.Time.
  • +
  • If the receiver is a basic type such as int, float64, or string.
  • +
+

Of course, it’s impossible to be exhaustive, as there will always be edge cases, but this section’s goal was to provide guidance to cover most cases. By default, we can choose to go with a value receiver unless there’s a good reason not to do so. In doubt, we should use a pointer receiver.

+

Source code

+

Never using named result parameters (#43)

+
+TL;DR +

Using named result parameters can be an efficient way to improve the readability of a function/method, especially if multiple result parameters have the same type. In some cases, this approach can also be convenient because named result parameters are initialized to their zero value. But be cautious about potential side effects.

+
+

When we return parameters in a function or a method, we can attach names to these parameters and use them as regular variables. When a result parameter is named, it’s initialized to its zero value when the function/method begins. With named result parameters, we can also call a naked return statement (without arguments). In that case, the current values of the result parameters are used as the returned values.

+

Here’s an example that uses a named result parameter b:

+
func f(a int) (b int) {
+    b = a
+    return
+}
+
+

In this example, we attach a name to the result parameter: b. When we call return without arguments, it returns the current value of b.

+

In some cases, named result parameters can also increase readability: for example, if two parameters have the same type. In other cases, they can also be used for convenience. Therefore, we should use named result parameters sparingly when there’s a clear benefit.

+

Source code

+

Unintended side effects with named result parameters (#44)

+
+TL;DR +

See #43.

+
+

We mentioned why named result parameters can be useful in some situations. But as these result parameters are initialized to their zero value, using them can sometimes lead to subtle bugs if we’re not careful enough. For example, can you spot what’s wrong with this code?

+
func (l loc) getCoordinates(ctx context.Context, address string) (
+    lat, lng float32, err error) {
+    isValid := l.validateAddress(address) (1)
+    if !isValid {
+        return 0, 0, errors.New("invalid address")
+    }
+
+    if ctx.Err() != nil { (2)
+        return 0, 0, err
+    }
+
+    // Get and return coordinates
+}
+
+

The error might not be obvious at first glance. Here, the error returned in the if ctx.Err() != nil scope is err. But we haven’t assigned any value to the err variable. It’s still assigned to the zero value of an error type: nil. Hence, this code will always return a nil error.

+

When using named result parameters, we must recall that each parameter is initialized to its zero value. As we have seen in this section, this can lead to subtle bugs that aren’t always straightforward to spot while reading code. Therefore, let’s remain cautious when using named result parameters, to avoid potential side effects.

+

Source code

+

Returning a nil receiver (#45)

+
+TL;DR +

When returning an interface, be cautious about not returning a nil pointer but an explicit nil value. Otherwise, unintended consequences may occur and the caller will receive a non-nil value.

+
+ + +

Source code

+

Using a filename as a function input (#46)

+
+TL;DR +

Designing functions to receive io.Reader types instead of filenames improves the reusability of a function and makes testing easier.

+
+

Accepting a filename as a function input to read from a file should, in most cases, be considered a code smell (except in specific functions such as os.Open). Indeed, it makes unit tests more complex because we may have to create multiple files. It also reduces the reusability of a function (although not all functions are meant to be reused). Using the io.Reader interface abstracts the data source. Regardless of whether the input is a file, a string, an HTTP request, or a gRPC request, the implementation can be reused and easily tested.

+

Source code

+

Ignoring how defer arguments and receivers are evaluated (argument evaluation, pointer, and value receivers) (#47)

+
+TL;DR +

Passing a pointer to a defer function and wrapping a call inside a closure are two possible solutions to overcome the immediate evaluation of arguments and receivers.

+
+

In a defer function the arguments are evaluated right away, not once the surrounding function returns. For example, in this code, we always call notify and incrementCounter with the same status: an empty string.

+
const (
+    StatusSuccess  = "success"
+    StatusErrorFoo = "error_foo"
+    StatusErrorBar = "error_bar"
+)
+
+func f() error {
+    var status string
+    defer notify(status)
+    defer incrementCounter(status)
+
+    if err := foo(); err != nil {
+        status = StatusErrorFoo
+        return err
+    }
+
+    if err := bar(); err != nil {
+        status = StatusErrorBar
+        return err
+    }
+
+    status = StatusSuccess
+    return nil
+}
+
+

Indeed, we call notify(status) and incrementCounter(status) as defer functions. Therefore, Go will delay these calls to be executed once f returns with the current value of status at the stage we used defer, hence passing an empty string.

+

Two leading options if we want to keep using defer.

+

The first solution is to pass a string pointer:

+
func f() error {
+    var status string
+    defer notify(&status) 
+    defer incrementCounter(&status)
+
+    // The rest of the function unchanged
+}
+
+

Using defer evaluates the arguments right away: here, the address of status. Yes, status itself is modified throughout the function, but its address remains constant, regardless of the assignments. Hence, if notify or incrementCounter uses the value referenced by the string pointer, it will work as expected. But this solution requires changing the signature of the two functions, which may not always be possible.

+

There’s another solution: calling a closure (an anonymous function value that references variables from outside its body) as a defer statement:

+
func f() error {
+    var status string
+    defer func() {
+        notify(status)
+        incrementCounter(status)
+    }()
+
+    // The rest of the function unchanged
+}
+
+

Here, we wrap the calls to both notify and incrementCounter within a closure. This closure references the status variable from outside its body. Therefore, status is evaluated once the closure is executed, not when we call defer. This solution also works and doesn’t require notify and incrementCounter to change their signature.

+

Let's also note this behavior applies with method receiver: the receiver is evaluated immediately.

+

Source code

+

Error Management

+

Panicking (#48)

+
+TL;DR +

Using panic is an option to deal with errors in Go. However, it should only be used sparingly in unrecoverable conditions: for example, to signal a programmer error or when you fail to load a mandatory dependency.

+
+

In Go, panic is a built-in function that stops the ordinary flow:

+
func main() {
+    fmt.Println("a")
+    panic("foo")
+    fmt.Println("b")
+}
+
+

This code prints a and then stops before printing b:

+
a
+panic: foo
+
+goroutine 1 [running]:
+main.main()
+        main.go:7 +0xb3
+
+

Panicking in Go should be used sparingly. There are two prominent cases, one to signal a programmer error (e.g., sql.Register that panics if the driver is nil or has already been register) and another where our application fails to create a mandatory 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

+

Ignoring when to wrap an error (#49)

+
+TL;DR +

Wrapping an error allows you to mark an error and/or provide additional context. However, error wrapping creates potential coupling as it makes the source error available for the caller. If you want to prevent that, don’t use error wrapping.

+
+

Since Go 1.13, the %w directive allows us to wrap errors conveniently. Error wrapping is about wrapping or packing an error inside a wrapper container that also makes the source error available. In general, the two main use cases for error wrapping are the following:

+
    +
  • Adding additional context to an error
  • +
  • Marking an error as a specific error
  • +
+

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

+

Comparing an error type inaccurately (#50)

+
+TL;DR +

If you use Go 1.13 error wrapping with the %w directive and fmt.Errorf, comparing an error against a type has to be done using errors.As. Otherwise, if the returned error you want to check is wrapped, it will fail the checks.

+
+ + +

Source code

+

Comparing an error value inaccurately (#51)

+
+TL;DR +

If you use Go 1.13 error wrapping with the %w directive and fmt.Errorf, comparing an error against or a value has to be done using errors.As. Otherwise, if the returned error you want to check is wrapped, it will fail the checks.

+
+

A sentinel error is an error defined as a global variable:

+
import "errors"
+
+var ErrFoo = errors.New("foo")
+
+

In general, the convention is to start with Err followed by the error type: here, ErrFoo. A sentinel error conveys an expected error, an error that clients will expect to check. As general guidelines:

+
    +
  • Expected errors should be designed as error values (sentinel errors): var ErrFoo = errors.New("foo").
  • +
  • Unexpected errors should be designed as error types: type BarError struct { ... }, with BarError implementing the error interface.
  • +
+

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

+

Handling an error twice (#52)

+
+TL;DR +

In most situations, an error should be handled only once. Logging an error is handling an error. Therefore, you have to choose between logging or returning an error. In many cases, error wrapping is the solution as it allows you to provide additional context to an error and return the source error.

+
+

Handling an error multiple times is a mistake made frequently by developers, not specifically in Go. This can cause situations where the same error is logged multiple times make debugging harder.

+

Let's remind 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 wrapping is the most convenient approach as it allows us to propagate the source error and add context to an error.

+

Source code

+

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

+

Not handling defer errors (#54)

+
+TL;DR +

In many cases, you shouldn’t ignore an error returned by a defer function. Either handle it directly or propagate it to the caller, depending on the context. If you want to ignore it, use the blank identifier.

+
+

Consider the following code:

+
func f() {
+  // ...
+  notify() // Error handling is omitted
+}
+
+func notify() error {
+  // ...
+}
+
+

From a maintainability perspective, the code can lead to some issues. Let’s consider a new reader looking at it. This reader notices that notify returns an error but that the error isn’t handled by the parent function. How can they guess whether or not handling the error was intentional? How can they know whether the previous developer forgot to handle it or did it purposely?

+

For these reasons, when we want to ignore an error, there's only one way to do it, using the blank identifier (_):

+
_ = notify
+
+

In terms of compilation and run time, this approach doesn’t change anything compared to the first piece of code. But this new version makes explicit that we aren’t interested in the error. Also, we can add a comment that indicates the rationale for why an error is ignored:

+
// At-most once delivery.
+// Hence, it's accepted to miss some of them in case of errors.
+_ = notify()
+
+

Source code

+

Concurrency: Foundations

+

Mixing up concurrency and parallelism (#55)

+
+TL;DR +

Understanding the fundamental differences between concurrency and parallelism is a cornerstone of the Go developer’s knowledge. Concurrency is about structure, whereas parallelism is about execution.

+
+

Concurrency and parallelism are not the same:

+
    +
  • Concurrency is about structure. We can change a sequential implementation into a concurrent one by introducing different steps that separate concurrent goroutines can tackle.
  • +
  • Meanwhile, parallelism is about execution. We can use parallism at the steps level by adding more parallel goroutines.
  • +
+

In summary, concurrency provides a structure to solve a problem with parts that may be parallelized. Therefore, concurrency enables parallelism.

+ + +

Thinking concurrency is always faster (#56)

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

+
+

Read the full section here.

+

Source code

+

Being puzzled about when to use channels or mutexes (#57)

+
+TL;DR +

Being aware of goroutine interactions can also be helpful when deciding between channels and mutexes. In general, parallel goroutines require synchronization and hence mutexes. Conversely, concurrent goroutines generally require coordination and orchestration and hence channels.

+
+

Given a concurrency problem, it may not always be clear whether we can implement a +solution using channels or mutexes. Because Go promotes sharing memory by communication, one mistake could be to always force the use of channels, regardless of +the use case. However, we should see the two options as complementary.

+

When should we use channels or mutexes? We will use the example in the next figure as a backbone. Our example has three different goroutines with specific relationships:

+
    +
  • G1 and G2 are parallel goroutines. They may be two goroutines executing the same function that keeps receiving messages from a channel, or perhaps two goroutines executing the same HTTP handler at the same time.
  • +
  • On the other hand, G1 and G3 are concurrent goroutines, as are G2 and G3. All the goroutines are part of an overall concurrent structure, but G1 and G2 perform the first step, whereas G3 does the next step.
  • +
+ + +

In general, parallel goroutines have to synchronize: for example, when they need to access or mutate a shared resource such as a slice. Synchronization is enforced with mutexes but not with any channel types (not with buffered channels). Hence, in general, synchronization between parallel goroutines should be achieved via mutexes.

+

Conversely, in general, concurrent goroutines have to coordinate and orchestrate. For example, if G3 needs to aggregate results from both G1 and G2, G1 and G2 need to signal to G3 that a new intermediate result is available. This coordination falls under the scope of communication—therefore, channels.

+

Regarding concurrent goroutines, there’s also the case where we want to transfer the ownership of a resource from one step (G1 and G2) to another (G3); for example, if G1 and G2 are enriching a shared resource and at some point, we consider this job as complete. Here, we should use channels to signal that a specific resource is ready and handle the ownership transfer.

+

Mutexes and channels have different semantics. Whenever we want to share a state or access a shared resource, mutexes ensure exclusive access to this resource. Conversely, channels are a mechanic for signaling with or without data (chan struct{} or not). Coordination or ownership transfer should be achieved via channels. It’s important to know whether goroutines are parallel or concurrent because, in general, we need mutexes for parallel goroutines and channels for concurrent ones.

+

Not understanding race problems (data races vs. race conditions and the Go memory model) (#58)

+
+TL;DR +

Being proficient in concurrency also means understanding that data races and race conditions are different concepts. Data races occur when multiple goroutines simultaneously access the same memory location and at least one of them is writing. Meanwhile, being data-race-free doesn’t necessarily mean deterministic execution. When a behavior depends on the sequence or the timing of events that can’t be controlled, this is a race condition.

+
+

Race problems can be among the hardest and most insidious bugs a programmer can face. As Go developers, we must understand crucial aspects such as data races and race conditions, their possible impacts, and how to avoid them.

+

Data Race

+

A data race occurs when two or more goroutines simultaneously access the same memory location and at least one is writing. In this case, the result can be hazardous. Even worse, in some situations, the memory location may end up holding a value containing a meaningless combination of bits.

+

We can prevent a data race from happening using different techniques. For example:

+
    +
  • Using the sync/atomic package
  • +
  • In synchronizing the two goroutines with an ad hoc data structure like a mutex
  • +
  • Using channels to make the two goroutines communicating to ensure that a variable is updated by only one goroutine at a time
  • +
+

Race Condition

+

Depending on the operation we want to perform, does a data-race-free application necessarily mean a deterministic result? Not necessarily.

+

A race condition occurs when the behavior depends on the sequence or the timing of events that can’t be controlled. Here, the timing of events is the goroutines’ execution order.

+

In summary, when we work in concurrent applications, it’s essential to understand that a data race is different from a race condition. A data race occurs when multiple goroutines simultaneously access the same memory location and at least one of them is writing. A data race means unexpected behavior. However, a data-race-free application doesn’t necessarily mean deterministic results. An application can be free of data races but still have behavior that depends on uncontrolled events (such as goroutine execution, how fast a message is published to a channel, or how long a call to a database lasts); this is a race condition. Understanding both concepts is crucial to becoming proficient in designing concurrent applications.

+

Source code

+

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.

+
+

In programming, the execution time of a workload is limited by one of the following:

+
    +
  • The speed of the CPU—For example, running a merge sort algorithm. The workload is called CPU-bound.
  • +
  • The speed of I/O—For example, making a REST call or a database query. The workload is called I/O-bound.
  • +
  • The amount of available memory—The workload is called memory-bound.
  • +
+
+Note +

The last is the rarest nowadays, given that memory has become very cheap in recent decades. Hence, this section focuses on the two first workload types: CPU- and I/O-bound.

+
+

If the workload executed by the workers is I/O-bound, the value mainly depends on the external system. Conversely, if the workload is CPU-bound, the optimal number of goroutines is close to the number of available CPU cores (a best practice can be to use runtime.GOMAXPROCS). Knowing the workload type (I/O or CPU) is crucial when designing concurrent applications.

+

Source code

+

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.

+
+
+

https://pkg.go.dev/context

+

A Context carries a deadline, a cancellation signal, and other values across API boundaries.

+
+

Deadline

+

A deadline refers to a specific point in time determined with one of the following:

+
    +
  • A time.Duration from now (for example, in 250 ms)
  • +
  • A time.Time (for example, 2023-02-07 00:00:00 UTC)
  • +
+

The semantics of a deadline convey that an ongoing activity should be stopped if this deadline is met. An activity is, for example, an I/O request or a goroutine waiting to receive a message from a channel.

+

Cancellation signals

+

Another use case for Go contexts is to carry a cancellation signal. Let’s imagine that we want to create an application that calls CreateFileWatcher(ctx context.Context, filename string) within another goroutine. This function creates a specific file watcher that keeps reading from a file and catches updates. When the provided context expires or is canceled, this function handles it to close the file descriptor.

+

Context values

+

The last use case for Go contexts is to carry a key-value list. What’s the point of having a context carrying a key-value list? Because Go contexts are generic and mainstream, there are infinite use cases.

+

For example, if we use tracing, we may want different subfunctions to share the same correlation ID. Some developers may consider this ID too invasive to be part of the function signature. In this regard, we could also decide to include it as part of the provided context.

+

Catching a context cancellation

+

The context.Context type exports a Done method that returns a receive-only notification channel: <-chan struct{}. This channel is closed when the work associated with the context should be canceled. For example,

+
    +
  • The Done channel related to a context created with context.WithCancel is closed when the cancel function is called.
  • +
  • The Done channel related to a context created with context.WithDeadline is closed when the deadline has expired.
  • +
+

One thing to note is that the internal channel should be closed when a context is canceled or has met a deadline, instead of when it receives a specific value, because the closure of a channel is the only channel action that all the consumer goroutines will receive. This way, all the consumers will be notified once a context is canceled or a deadline is reached.

+

In summary, to be a proficient Go developer, we have to understand what a context is and how to use it. In general, a function that users wait for should take a context, as doing so allows upstream callers to decide when calling this function should be aborted.

+

Source code

+

Concurrency: Practice

+

Propagating an inappropriate context (#61)

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

+
+

In many situations, it is recommended to propagate Go contexts. However, context propagation can sometimes lead to subtle bugs, preventing subfunctions from being correctly executed.

+

Let’s consider the following example. We expose an HTTP handler that performs some tasks and returns a response. But just before returning the response, we also want to send it to a Kafka topic. We don’t want to penalize the HTTP consumer latency-wise, so we want the publish action to be handled asynchronously within a new goroutine. We assume that we have at our disposal a publish function that accepts a context so the action of publishing a message can be interrupted if the context is canceled, for example. Here is a possible implementation:

+
func handler(w http.ResponseWriter, r *http.Request) {
+    response, err := doSomeTask(r.Context(), r)
+    if err != nil {
+        http.Error(w, err.Error(), http.StatusInternalServerError)
+    return
+    }
+    go func() {
+        err := publish(r.Context(), response)
+        // Do something with err
+    }()
+    writeResponse(response)
+}
+
+

What’s wrong with this piece of code? We have to know that the context attached to an HTTP request can cancel in different conditions:

+
    +
  • When the client’s connection closes
  • +
  • In the case of an HTTP/2 request, when the request is canceled
  • +
  • When the response has been written back to the client
  • +
+

In the first two cases, we probably handle things correctly. For example, if we get a response from doSomeTask but the client has closed the connection, it’s probably OK to call publish with a context already canceled so the message isn’t published. But what about the last case?

+

When the response has been written to the client, the context associated with the request will be canceled. Therefore, we are facing a race condition:

+
    +
  • If the response is written after the Kafka publication, we both return a response and publish a message successfully
  • +
  • However, if the response is written before or during the Kafka publication, the message shouldn’t be published.
  • +
+

In the latter case, calling publish will return an error because we returned the HTTP response quickly.

+
+Note +

From Go 1.21, there is a way to create a new context without cancel. context.WithoutCancel returns a copy of parent that is not canceled when parent is canceled.

+
+

In summary, propagating a context should be done cautiously.

+

Source code

+

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.

+
+

Goroutines are easy and cheap to start—so easy and cheap that we may not necessarily have a plan for when to stop a new goroutine, which can lead to leaks. Not knowing when to stop a goroutine is a design issue and a common concurrency mistake in Go.

+

Let’s discuss a concrete example. We will design an application that needs to watch some external configuration (for example, using a database connection). Here’s a first implementation:

+
func main() {
+    newWatcher()
+    // Run the application
+}
+
+type watcher struct { /* Some resources */ }
+
+func newWatcher() {
+    w := watcher{}
+    go w.watch() // Creates a goroutine that watches some external configuration
+}
+
+

The problem with this code is that when the main goroutine exits (perhaps because of an OS signal or because it has a finite workload), the application is stopped. Hence, the resources created by watcher aren’t closed gracefully. How can we prevent this from happening?

+

One option could be to pass to newWatcher a context that will be canceled when main returns:

+
func main() {
+    ctx, cancel := context.WithCancel(context.Background())
+    defer cancel()
+    newWatcher(ctx)
+    // Run the application
+}
+
+func newWatcher(ctx context.Context) {
+    w := watcher{}
+    go w.watch(ctx)
+}
+
+

We propagate the context created to the watch method. When the context is canceled, the watcher struct should close its resources. However, can we guarantee that watch will have time to do so? Absolutely not—and that’s a design flaw.

+

The problem is that we used signaling to convey that a goroutine had to be stopped. We didn’t block the parent goroutine until the resources had been closed. Let’s make sure we do:

+
func main() {
+    w := newWatcher()
+    defer w.close()
+    // Run the application
+}
+
+func newWatcher() watcher {
+    w := watcher{}
+    go w.watch()
+    return w
+}
+
+func (w watcher) close() {
+    // Close the resources
+}
+
+

Instead of signaling watcher that it’s time to close its resources, we now call this close method, using defer to guarantee that the resources are closed before the application exits.

+

In summary, let’s be mindful that a goroutine is a resource like any other that must eventually be closed to free memory or other resources. Starting a goroutine without knowing when to stop it is a design issue. Whenever a goroutine is started, we should have a clear plan about when it will stop. Last but not least, if a goroutine creates resources and its lifetime is bound to the lifetime of the application, it’s probably safer to wait for this goroutine to complete before exiting the application. This way, we can ensure that the resources can be freed.

+

Source code

+

⚠ Not being careful with goroutines and loop variables (#63)

+
+Warning +

This mistake isn't relevant anymore from Go 1.22 (details).

+
+

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.

+
+

One common mistake made by Go developers while working with channels is to make wrong assumptions about how select behaves with multiple channels.

+

For example, let's consider the following case (disconnectCh is a unbuffered channel):

+
go func() {
+  for i := 0; i < 10; i++ {
+      messageCh <- i
+    }
+    disconnectCh <- struct{}{}
+}()
+
+for {
+    select {
+    case v := <-messageCh:
+        fmt.Println(v)
+    case <-disconnectCh:
+        fmt.Println("disconnection, return")
+        return
+    }
+}
+
+

If we run this example multiple times, the result will be random:

+
0
+1
+2
+disconnection, return
+
+0
+disconnection, return
+
+

Instead of consuming the 10 messages, we only received a few of them. What’s the reason? It lies in the specification of the select statement with multiple channels (https:// go.dev/ref/spec):

+
+

Quote

+

If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection.

+
+

Unlike a switch statement, where the first case with a match wins, the select statement selects randomly if multiple options are possible.

+

This behavior might look odd at first, but there’s a good reason for it: to prevent possible starvation. Suppose the first possible communication chosen is based on the source order. In that case, we may fall into a situation where, for example, we only receive from one channel because of a fast sender. To prevent this, the language designers decided to use a random selection.

+

When using select with multiple channels, we must remember that if multiple options are possible, the first case in the source order does not automatically win. Instead, Go selects randomly, so there’s no guarantee about which option will be chosen. To overcome this behavior, in the case of a single producer goroutine, we can use either unbuffered channels or a single channel.

+

Source code

+

Not using notification channels (#65)

+
+TL;DR +

Send notifications using a chan struct{} type.

+
+

Channels are a mechanism for communicating across goroutines via signaling. A signal can be either with or without data.

+

Let’s look at a concrete example. We will create a channel that will notify us whenever a certain disconnection occurs. One idea is to handle it as a chan bool:

+
disconnectCh := make(chan bool)
+
+

Now, let’s say we interact with an API that provides us with such a channel. Because it’s a channel of Booleans, we can receive either true or false messages. It’s probably clear what true conveys. But what does false mean? Does it mean we haven’t been disconnected? And in this case, how frequently will we receive such a signal? Does it mean we have reconnected? Should we even expect to receive false? Perhaps we should only expect to receive true messages.

+

If that’s the case, meaning we don’t need a specific value to convey some information, we need a channel without data. The idiomatic way to handle it is a channel of empty structs: chan struct{}.

+

Not using nil channels (#66)

+
+TL;DR +

Using nil channels should be part of your concurrency toolset because it allows you to remove cases from select statements, for example.

+
+

What should this code do?

+
var ch chan int
+<-ch
+
+

ch is a chan int type. The zero value of a channel being nil, ch is nil. The goroutine won’t panic; however, it will block forever.

+

The principle is the same if we send a message to a nil channel. This goroutine blocks forever:

+
var ch chan int
+ch <- 0
+
+

Then what’s the purpose of Go allowing messages to be received from or sent to a nil channel? For example, we can use nil channels to implement an idiomatic way to merge two channels:

+
func merge(ch1, ch2 <-chan int) <-chan int {
+    ch := make(chan int, 1)
+
+    go func() {
+        for ch1 != nil || ch2 != nil { // Continue if at least one channel isn’t nil
+            select {
+            case v, open := <-ch1:
+                if !open {
+                    ch1 = nil // Assign ch1 to a nil channel once closed
+                    break
+                }
+                ch <- v
+            case v, open := <-ch2:
+                if !open {
+                    ch2 = nil // Assigns ch2 to a nil channel once closed
+                    break
+                }
+                ch <- v
+            }
+        }
+        close(ch)
+    }()
+
+    return ch
+}
+
+

This elegant solution relies on nil channels to somehow remove one case from the select statement.

+

Let’s keep this idea in mind: nil channels are useful in some conditions and should be part of the Go developer’s toolset when dealing with concurrent code.

+

Source code

+

Being puzzled about channel size (#67)

+
+TL;DR +

Carefully decide on the right channel type to use, given a problem. Only unbuffered channels provide strong synchronization guarantees. For buffered channels, you should have a good reason to specify a channel size other than one.

+
+

An unbuffered channel is a channel without any capacity. It can be created by either omitting the size or providing a 0 size:

+
ch1 := make(chan int)
+ch2 := make(chan int, 0)
+
+

With an unbuffered channel (sometimes called a synchronous channel), the sender will block until the receiver receives data from the channel.

+

Conversely, a buffered channel has a capacity, and it must be created with a size greater than or equal to 1:

+
ch3 := make(chan int, 1)
+
+

With a buffered channel, a sender can send messages while the channel isn’t full. Once the channel is full, it will block until a receiver goroutine receives a message:

+
ch3 := make(chan int, 1)
+ch3 <-1 // Non-blocking
+ch3 <-2 // Blocking
+
+

The first send isn’t blocking, whereas the second one is, as the channel is full at this stage.

+

What's the main difference between unbuffered and buffered channels:

+
    +
  • An unbuffered channel enables synchronization. We have the guarantee that two goroutines will be in a known state: one receiving and another sending a message.
  • +
  • A buffered channel doesn’t provide any strong synchronization. Indeed, a producer goroutine can send a message and then continue its execution if the channel isn’t full. The only guarantee is that a goroutine won’t receive a message before it is sent. But this is only a guarantee because of causality (you don’t drink your coffee before you prepare it).
  • +
+

If we need a buffered channel, what size should we provide?

+

The default value we should use for buffered channels is its minimum: 1. So, we may approach the problem from this standpoint: is there any good reason not to use a value of 1? Here’s a list of possible cases where we should use another size:

+
    +
  • While using a worker pooling-like pattern, meaning spinning a fixed number of goroutines that need to send data to a shared channel. In that case, we can tie the channel size to the number of goroutines created.
  • +
  • When using channels for rate-limiting problems. For example, if we need to enforce resource utilization by bounding the number of requests, we should set up the channel size according to the limit.
  • +
+

If we are outside of these cases, using a different channel size should be done cautiously. Let’s bear in mind that deciding about an accurate queue size isn’t an easy problem:

+
+

Martin Thompson

+

Queues are typically always close to full or close to empty due to the differences in pace between consumers and producers. They very rarely operate in a balanced middle ground where the rate of production and consumption is evenly matched.

+
+

Forgetting about possible side effects with string formatting (#68)

+
+TL;DR +

Being aware that string formatting may lead to calling existing functions means watching out for possible deadlocks and other data races.

+
+

It’s pretty easy to forget the potential side effects of string formatting while working in a concurrent application.

+

etcd data race

+

github.com/etcd-io/etcd/pull/7816 shows an example of an issue where a map's key was formatted based on a mutable values from a context.

+

Deadlock

+

Can you see what the problem is in this code with a Customer struct exposing an UpdateAge method and implementing the fmt.Stringer interface?

+
type Customer struct {
+    mutex sync.RWMutex // Uses a sync.RWMutex to protect concurrent accesses
+    id    string
+    age   int
+}
+
+func (c *Customer) UpdateAge(age int) error {
+    c.mutex.Lock() // Locks and defers unlock as we update Customer
+    defer c.mutex.Unlock()
+
+    if age < 0 { // Returns an error if age is negative
+        return fmt.Errorf("age should be positive for customer %v", c)
+    }
+
+    c.age = age
+    return nil
+}
+
+func (c *Customer) String() string {
+    c.mutex.RLock() // Locks and defers unlock as we read Customer
+    defer c.mutex.RUnlock()
+    return fmt.Sprintf("id %s, age %d", c.id, c.age)
+}
+
+

The problem here may not be straightforward. If the provided age is negative, we return an error. Because the error is formatted, using the %s directive on the receiver, it will call the String method to format Customer. But because UpdateAge already acquires the mutex lock, the String method won’t be able to acquire it. Hence, this leads to a deadlock situation. If all goroutines are also asleep, it leads to a panic.

+

One possible solution is to restrict the scope of the mutex lock:

+
func (c *Customer) UpdateAge(age int) error {
+    if age < 0 {
+        return fmt.Errorf("age should be positive for customer %v", c)
+    }
+
+    c.mutex.Lock()
+    defer c.mutex.Unlock()
+
+    c.age = age
+    return nil
+}
+
+

Yet, such an approach isn't always possible. In these conditions, we have to be extremely careful with string formatting.

+

Another approach is to access the id field directly:

+
func (c *Customer) UpdateAge(age int) error {
+    c.mutex.Lock()
+    defer c.mutex.Unlock()
+
+    if age < 0 {
+        return fmt.Errorf("age should be positive for customer id %s", c.id)
+    }
+
+    c.age = age
+    return nil
+}
+
+

In concurrent applications, we should remain cautious about the possible side effects of string formatting.

+

Source code

+

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.

+
+

Should adding an element to a slice using append is data-race-free? Spoiler: it depends.

+

Do you believe this example has a data race?

+
s := make([]int, 1)
+
+go func() { // In a new goroutine, appends a new element on s
+    s1 := append(s, 1)
+    fmt.Println(s1)
+}()
+
+go func() { // Same
+    s2 := append(s, 1)
+    fmt.Println(s2)
+}()
+
+

The answer is no.

+

In this example, we create a slice with make([]int, 1). The code creates a one-length, one-capacity slice. Thus, because the slice is full, using append in each goroutine returns a slice backed by a new array. It doesn’t mutate the existing array; hence, it doesn’t lead to a data race.

+

Now, let’s run the same example with a slight change in how we initialize s. Instead of creating a slice with a length of 1, we create it with a length of 0 but a capacity of 1. How about this new example? Does it contain a data race?

+
s := make([]int, 0, 1)
+
+go func() { 
+    s1 := append(s, 1)
+    fmt.Println(s1)
+}()
+
+go func() {
+    s2 := append(s, 1)
+    fmt.Println(s2)
+}()
+
+

The answer is yes. We create a slice with make([]int, 0, 1). Therefore, the array isn’t full. Both goroutines attempt to update the same index of the backing array (index 1), which is a data race.

+

How can we prevent the data race if we want both goroutines to work on a slice containing the initial elements of s plus an extra element? One solution is to create a copy of s.

+

We should remember that using append on a shared slice in concurrent applications can lead to a data race. Hence, it should be avoided.

+

Source code

+

Using mutexes inaccurately with slices and maps (#70)

+
+TL;DR +

Remembering that slices and maps are pointers can prevent common data races.

+
+

Let's implement a Cache struct used to handle caching for customer balances. This struct will contain a map of balances per customer ID and a mutex to protect concurrent accesses:

+
type Cache struct {
+    mu       sync.RWMutex
+    balances map[string]float64
+}
+
+

Next, we add an AddBalance method that mutates the balances map. The mutation is done in a critical section (within a mutex lock and a mutex unlock):

+
func (c *Cache) AddBalance(id string, balance float64) {
+    c.mu.Lock()
+    c.balances[id] = balance
+    c.mu.Unlock()
+}
+
+

Meanwhile, we have to implement a method to calculate the average balance for all the customers. One idea is to handle a minimal critical section this way:

+
func (c *Cache) AverageBalance() float64 {
+    c.mu.RLock()
+    balances := c.balances // Creates a copy of the balances map
+    c.mu.RUnlock()
+
+    sum := 0.
+    for _, balance := range balances { // Iterates over the copy, outside of the critical section
+        sum += balance
+    }
+    return sum / float64(len(balances))
+}
+
+

What's the problem with this code?

+

If we run a test using the -race flag with two concurrent goroutines, one calling AddBalance (hence mutating balances) and another calling AverageBalance, a data race occurs. What’s the problem here?

+

Internally, a map is a runtime.hmap struct containing mostly metadata (for example, a counter) and a pointer referencing data buckets. So, balances := c.balances doesn’t copy the actual data. Therefore, the two goroutines perform operations on the same data set, and one mutates it. Hence, it's a data race.

+

One possible solution is to protect the whole AverageBalance function:

+
func (c *Cache) AverageBalance() float64 {
+    c.mu.RLock()
+    defer c.mu.RUnlock() // Unlocks when the function returns
+
+    sum := 0.
+    for _, balance := range c.balances {
+        sum += balance
+    }
+    return sum / float64(len(c.balances))
+}
+
+

Another option, if the iteration operation isn’t lightweight, is to work on an actual copy of the data and protect only the copy:

+
func (c *Cache) AverageBalance() float64 {
+    c.mu.RLock()
+    m := make(map[string]float64, len(c.balances)) // Copies the map
+    for k, v := range c.balances {
+        m[k] = v
+    }
+    c.mu.RUnlock()
+
+    sum := 0.
+    for _, balance := range m {
+        sum += balance
+    }
+    return sum / float64(len(m))
+}
+
+

Once we have made a deep copy, we release the mutex. The iterations are done on the copy outside of the critical section.

+

In summary, we have to be careful with the boundaries of a mutex lock. In this section, we have seen why assigning an existing map (or an existing slice) to a map isn’t enough to protect against data races. The new variable, whether a map or a slice, is backed by the same data set. There are two leading solutions to prevent this: protect the whole function, or work on a copy of the actual data. In all cases, let’s be cautious when designing critical sections and make sure the boundaries are accurately defined.

+

Source code

+

Misusing sync.WaitGroup (#71)

+
+TL;DR +

To accurately use sync.WaitGroup, call the Add method before spinning up goroutines.

+
+

Source code

+

Forgetting about sync.Cond (#72)

+
+TL;DR +

You can send repeated notifications to multiple goroutines with sync.Cond.

+
+

Source code

+

Not using errgroup (#73)

+
+TL;DR +

You can synchronize a group of goroutines and handle errors and contexts with the errgroup package.

+
+

Source code

+

Copying a sync type (#74)

+
+TL;DR +

sync types shouldn’t be copied.

+
+

Source code

+

Standard Library

+

Providing a wrong time duration (#75)

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

+
+

Many common functions in the standard library accept a time.Duration, which is an alias for the int64 type. However, one time.Duration unit represents one nanosecond, instead of one millisecond, as commonly seen in other programming languages. As a result, passing numeric types instead of using the time.Duration API can lead to unexpected behavior.

+

A developer with experience in other languages might assume that the following code creates a new time.Ticker that delivers ticks every second, given the value 1000:

+
ticker := time.NewTicker(1000)
+for {
+    select {
+    case <-ticker.C:
+        // Do something
+    }
+}
+
+

However, because 1,000 time.Duration units = 1,000 nanoseconds, ticks are delivered every 1,000 nanoseconds = 1 microsecond, not every second as assumed.

+

We should always use the time.Duration API to avoid confusion and unexpected behavior: +

ticker = time.NewTicker(time.Microsecond)
+// Or
+ticker = time.NewTicker(1000 * time.Nanosecond)
+

+

Source code

+

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.

+
+

Developers often use time.After in loops or HTTP handlers repeatedly to implement the timing function. But it can lead to unintended peak memory consumption due to the delayed release of resources, just like the following code:

+
func consumer(ch <-chan Event) {
+    for {
+        select {
+        case event := <-ch:
+            handle(event)
+        case <-time.After(time.Hour):
+            log.Println("warning: no messages received")
+        }
+    }
+}
+
+

The source code of the function time.After is as follows:

+
func After(d Duration) <-chan Time {
+    return NewTimer(d).C
+}
+
+

As we see, it returns receive-only channel.

+

When time.After is used in a loop or repeated context, a new channel is created in each iteration. If these channels are not properly closed or if their associated timers are not stopped, they can accumulate and consume memory. The resources associated with each timer and channel are only released when the timer expires or the channel is closed.

+

To avoid this happening, We can use context's timeout setting instead of time.After, like below:

+
func consumer(ch <-chan Event) {
+    for {
+        ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
+        select {
+        case event := <-ch:
+            cancel()
+            handle(event)
+        case <-ctx.Done():
+            log.Println("warning: no messages received")
+        }
+    }
+}
+
+

We can also use time.NewTimer like so:

+
func consumer(ch <-chan Event) {
+    timerDuration := 1 * time.Hour
+    timer := time.NewTimer(timerDuration)
+
+    for {
+        timer.Reset(timerDuration)
+        select {
+        case event := <-ch:
+            handle(event)
+        case <-timer.C:
+            log.Println("warning: no messages received")
+        }
+    }
+}
+
+

Source code

+

JSON handling common mistakes (#77)

+
    +
  • Unexpected behavior because of type embedding
  • +
+

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

+
    +
  • 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

+
    +
  • 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

+

Common SQL mistakes (#78)

+
    +
  • Forgetting that sql.Open doesn't necessarily establish connections to a database
  • +
+

Call the Ping or PingContext method if you need to test your configuration and make sure a database is reachable.

+

Source code

+
    +
  • Forgetting about connections pooling
  • +
+

Configure the database connection parameters for production-grade applications.

+
    +
  • Not using prepared statements
  • +
+

Using SQL prepared statements makes queries more efficient and more secure.

+

Source code

+
    +
  • Mishandling null values
  • +
+

Deal with nullable columns in tables using pointers or sql.NullXXX types.

+

Source code

+
    +
  • 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

+

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

+

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.

+
+

Consider the following HTTP handler that handles an error from foo using http.Error:

+
func handler(w http.ResponseWriter, req *http.Request) {
+    err := foo(req)
+    if err != nil {
+        http.Error(w, "foo", http.StatusInternalServerError)
+    }
+
+    _, _ = w.Write([]byte("all good"))
+    w.WriteHeader(http.StatusCreated)
+}
+
+

If we run this code and err != nil, the HTTP response would be:

+
foo
+all good
+
+

The response contains both the error and success messages, and also the first HTTP status code, 500. There would also be a warning log indicating that we attempted to write the status code multiple times:

+
2023/10/10 16:45:33 http: superfluous response.WriteHeader call from main.handler (main.go:20)
+
+

The mistake in this code is that http.Error does not stop the handler's execution, which means the success message and status code get written in addition to the error. Beyond an incorrect response, failing to return after writing an error can lead to the unwanted execution of code and unexpected side-effects. The following code adds the return statement following the http.Error and exhibits the desired behavior when ran:

+
func handler(w http.ResponseWriter, req *http.Request) {
+    err := foo(req)
+    if err != nil {
+        http.Error(w, "foo", http.StatusInternalServerError)
+        return // Adds the return statement
+    }
+
+    _, _ = w.Write([]byte("all good"))
+    w.WriteHeader(http.StatusCreated)
+}
+
+

Source code

+

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

+

Testing

+

Not categorizing tests (build tags, environment variables, and short mode) (#82)

+
+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

+

Not enabling the race flag (#83)

+
+TL;DR +

Enabling the -race flag is highly recommended when writing concurrent applications. Doing so allows you to catch potential data races that can lead to software bugs.

+
+

In Go, the race detector isn’t a static analysis tool used during compilation; instead, it’s a tool to find data races that occur at runtime. To enable it, we have to enable the -race flag while compiling or running a test. For example:

+
go test -race ./...
+
+

Once the race detector is enabled, the compiler instruments the code to detect data races. Instrumentation refers to a compiler adding extra instructions: here, tracking all memory accesses and recording when and how they occur.

+

Enabling the race detector adds an overhead in terms of memory and execution time; hence, it's generally recommended to enable it only during local testing or continuous integration, not production.

+

If a race is detected, Go raises a warning. For example:

+
package main
+
+import (
+    "fmt"
+)
+
+func main() {
+    i := 0
+    go func() { i++ }()
+    fmt.Println(i)
+}
+
+

Runnig this code with the -race logs the following warning:

+
==================
+WARNING: DATA RACE
+Write at 0x00c000026078 by goroutine 7: # (1)
+  main.main.func1()
+      /tmp/app/main.go:9 +0x4e
+
+Previous read at 0x00c000026078 by main goroutine: # (2)
+  main.main()
+      /tmp/app/main.go:10 +0x88
+
+Goroutine 7 (running) created at: # (3)
+  main.main()
+      /tmp/app/main.go:9 +0x7a
+==================
+
+
    +
  1. Indicates that goroutine 7 was writing
  2. +
  3. Indicates that the main goroutine was reading
  4. +
  5. Indicates when the goroutine 7 was created
  6. +
+

Let’s make sure we are comfortable reading these messages. Go always logs the following:

+
    +
  • The concurrent goroutines that are incriminated: here, the main goroutine and goroutine 7.
  • +
  • Where accesses occur in the code: in this case, lines 9 and 10.
  • +
  • When these goroutines were created: goroutine 7 was created in main().
  • +
+

In addition, if a specific file contains tests that lead to data races, we can exclude it from race detection using the !race build tag:

+
//go:build !race
+
+package main
+
+import (
+    "testing"
+)
+
+func TestFoo(t *testing.T) {
+    // ...
+}
+
+

Not using test execution modes (parallel and shuffle) (#84)

+
+TL;DR +

Using the -parallel flag is an efficient way to speed up tests, especially long-running ones. Use the -shuffle flag to help ensure that a test suite doesn’t rely on wrong assumptions that could hide bugs.

+
+

Not using table-driven tests (#85)

+
+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

+

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

+

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

+

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

+
    +
  • The iotest package helps write io.Reader and test that an application is tolerant to errors.
  • +
+

Source code

+

Writing inaccurate benchmarks (#89)

+
+TL;DR +

Regarding benchmarks:

+
    +
  • Use time methods to preserve the accuracy of a benchmark.
  • +
  • Increasing benchtime or using tools such as benchstat can be helpful when dealing with micro-benchmarks.
  • +
  • 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.
  • +
  • Make sure the function under test leads to a side effect, to prevent compiler optimizations from fooling you about the benchmark results.
  • +
  • To prevent the observer effect, force a benchmark to re-create the data used by a CPU-bound function.
  • +
+
+

Read the full section here.

+

Source code

+

Not exploring all the Go testing features (#90)

+
    +
  • Code coverage
  • +
+

Use code coverage with the -coverprofile flag to quickly see which part of the code needs more attention.

+
    +
  • Testing from a different package
  • +
+

Place unit tests in a different package to enforce writing tests that focus on an exposed behavior, not internals.

+

Source code

+
    +
  • 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

+
    +
  • 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

+

Not using fuzzing (community mistake)

+
+TL;DR +

Fuzzing is an efficient strategy to detect random, unexpected, or malformed inputs to complex functions and methods in order to discover vulnerabilities, bugs, or even potential crashes.

+
+

Credits: @jeromedoucet

+

Optimizations

+

Not understanding CPU caches (#91)

+
    +
  • CPU architecture
  • +
+

Understanding how to use CPU caches is important for optimizing CPU-bound applications because the L1 cache is about 50 to 100 times faster than the main memory.

+
    +
  • Cache line
  • +
+

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

+
    +
  • Slice of structs vs. struct of slices
  • +
+ + +

Source code

+
    +
  • 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

+
    +
  • Cache placement policy
  • +
+

To avoid a critical stride, hence utilizing only a tiny portion of the cache, be aware that caches are partitioned.

+

Writing concurrent code that leads to false sharing (#92)

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

+
+

Read the full section here.

+

Source code

+

Not taking into account instruction-level parallelism (#93)

+
+TL;DR +

Use 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

+

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

+

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

+

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

+

Not relying on inlining (#97)

+
+TL;DR +

Use the fast-path inlining technique to efficiently reduce the amortized time to call a function.

+
+

Not using Go diagnostics tooling (#98)

+
+TL;DR +

Rely on profiling and the execution tracer to understand how an application performs and the parts to optimize.

+
+

Read the full section here.

+

Not understanding how the GC works (#99)

+
+TL;DR +

Understanding how to tune the GC can lead to multiple benefits such as handling sudden load increases more efficiently.

+
+

Not understanding the impacts of running Go in Docker and Kubernetes (#100)

+
+TL;DR +

To help avoid CPU throttling when deployed in Docker and Kubernetes, keep in mind that Go isn’t CFS-aware.

+
+

By default, GOMAXPROCS is set to the number of OS-apparent logical CPU cores.

+

When running some Go code inside Docker and Kubernetes, we must know that Go isn't CFS-aware (github.com/golang/go/issues/33803). Therefore, GOMAXPROCS isn't automatically set to the value of spec.containers.resources.limits.cpu (see Kubernetes Resource Management for Pods and Containers); instead, it's set to the number of logical cores on the host machine. The main implication is that it can lead to an increased tail latency in some specific situations.

+

One solution is to rely on uber-go/automaxprocs that automatically set GOMAXPROCS to match the Linux container CPU quota.

+

Community

+

Thanks to all the contributors:

+

+ Description of the image +

+ + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/ja/index.html b/ja/index.html new file mode 100644 index 00000000..0dc1ec51 --- /dev/null +++ b/ja/index.html @@ -0,0 +1,4984 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Japanese Version - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Go言語でありがちな間違い

+

このページは『100 Go Mistakes』の内容をまとめたものです。一方で、コミュニティに開かれたページでもあります。「ありがちな間違い」が新たに追加されるべきだとお考えでしたら community mistake issue を作成してください。

+
+Jobs +

Is your company hiring? Sponsor the Japanese version of this repository and let a significant audience of Go developers (~1k unique visitors per week) know about your opportunities in this section.

+
+

+
+注意 +

現在、大幅に多くのコンテンツを追加して強化している新しいバージョンを閲覧しています。このバージョンはまだ開発中です。問題を見つけた場合はどうぞ気軽にPRを作成してください。

+
+

コードとプロジェクト構成

+

意図的でない変数のシャドーイング (#1)

+
+要約 +

変数のシャドーイングを避けることは、誤った変数の参照や読み手の混乱を防ぎます。

+
+

変数のシャドーイングは、変数名がブロック内で再宣言されることで生じますが、これは間違いを引き起こしやすくします。変数のシャドーイングを禁止するかどうかは個人の好みによります。たとえば、エラーに対して err のような既存の変数名を再利用すると便利な場合があります。とはいえ、コードはコンパイルされたものの、値を受け取った変数が予期したものではないというシナリオに直面する可能性があるため、原則として引き続き注意を払う必要があります。

+

ソースコード

+

不必要にネストされたコード (#2)

+
+要約 +

ネストが深くならないようにし、ハッピーパスを左側に揃えることでメンタルコードモデルを構築することが容易になります。

+
+

一般的に、関数がより深いネストを要求するほど、読んで理解することがより複雑になります。私たちのコードの可読性を最適化するために、このルールの適用方法を見ていきましょう。

+
    +
  • if ブロックが返されるとき、すべての場合において else ブロックを省略する必要があります。 たとえば、次のように書くべきではありません。
  • +
+
if foo() {
+    // ...
+    return true
+} else {
+    // ...
+}
+
+

代わりに、次のように else ブロックを省略します。

+
if foo() {
+    // ...
+    return true
+}
+// ...
+
+
    +
  • ノンハッピーパスでもこのロジックに従うことが可能です。
  • +
+
if s != "" {
+    // ...
+} else {
+    return errors.New("empty string")
+}
+
+

ここでは、空の s がノンハッピーパスを表します。したがって、次のように条件をひっくり返す必要があります。

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

読みやすいコードを書くことは、すべての開発者にとって重要な課題です。ネストされたブロックの数を減らすよう努め、ハッピーパスを左側に揃え、できるだけ早く戻ることが、コードの可読性を向上させる具体的な手段です。

+

ソースコード

+

init関数の誤用 (#3)

+
+要約 +

変数を初期化するときは、init関数のエラー処理が制限されており、ステートの処理とテストがより複雑になることに注意してください。ほとんどの場合、初期化は特定の関数として処理されるべきです。

+
+

init関数は、アプリケーションのステートを初期化するために使用される関数です。引数を取らず、結果も返しません( func() 関数)。パッケージが初期化されると、パッケージ内のすべての定数および変数の宣言が評価されます。次に、init関数が実行されます。

+

init関数はいくつかの問題を引き起こす可能性があります。

+
    +
  • エラー処理が制限される可能性があります。
  • +
  • テストの実装方法が複雑になる可能性があります(たとえば、外部依存関係を設定する必要がありますが、単体テストの範囲では必要ない可能性があります)。
  • +
  • 初期化でステートを設定する必要がある場合は、グローバル変数を使用して行う必要があります。
  • +
+

init関数には注意が必要です。ただし、静的構成の定義など、状況によっては役立つ場合があります。それ以外のほとんどの場合、初期化処理は特定の関数を通じて行われるべきです。

+

ソースコード

+

ゲッターとセッターの乱用 (#4)

+
+要約 +

Go言語では、慣用的にゲッターとセッターの使用を強制することはありません。実利を重視し、効率性と特定の慣習に従うこととの間の適切なバランスを見つけることが、進むべき道であるはずです。

+
+

データのカプセル化とは、オブジェクトの値または状態を隠すことを指します。ゲッターとセッターは、エクスポートされていないオブジェクトフィールドの上にエクスポートされたメソッドを提供することでカプセル化を可能にする手段です。

+

Go言語では、一部の言語で見られるようなゲッターとセッターの自動サポートはありません。また、ゲッターとセッターを使用して構造体フィールドにアクセスすることは必須でも慣用的でもありません。値をもたらさない構造体のゲッターとセッターでコードを埋めるべきではありません。実利を重視し、他のプログラミングパラダイムで時には議論の余地がないと考えられている慣習に従うことと、効率性との間の適切なバランスを見つけるよう努めるべきです。

+

Go言語は、シンプルさを含む多くの特性を考慮して設計された独自の言語であることを忘れないでください。ただし、ゲッターとセッターの必要性が見つかった場合、または前述のように、前方互換性を保証しながら将来の必要性が予測される場合は、それらを使用することに問題はありません。

+

インタフェース汚染 (#5)

+
+要約 +

抽象化は作成されるべきものではなく、発見されるべきものです。不必要な複雑さを避けるために、インタフェースは、必要になると予測したときではなく、必要になったときに作成するか、少なくとも抽象化が有効であることを証明できる場合に作成してください。

+
+

インタフェースは、オブジェクトの動作を指定する方法を提供します。複数のオブジェクトが実装できる共通項を抽出するために、インタフェースは使用されます。Go言語のインタフェースが大きく異なるのは、暗黙的に満たされることです。オブジェクト X がインタフェース Y を実装していることを示す implements のような明示的なキーワードはありません。

+

一般に、インタフェースが価値をもたらすと考えられる主要な使用例は3つあります。それは、共通の動作を除外する、何らかの分離を作成する、および型を特定の動作に制限するというものです。ただし、このリストはすべてを網羅しているわけではなく、直面する状況によっても異なります。

+

多くの場合、インタフェースは抽象化するために作成されます。そして、プログラミングで抽象化するときの主な注意点は、抽象化は作成されるべきではなく、発見されるべきであるということを覚えておくことです。すなわち、そうする直接の理由がない限り、コード内で抽象化すべきではないということです。インタフェースを使って設計するのではなく、具体的なニーズを待つべきです。別の言い方をすれば、インタフェースは必要になると予測したときではなく、必要になったときに作成する必要があります。 +インタフェースの過度な使用をした場合の主な問題は何でしょうか。答えは、コードフローがより複雑になることです。役に立たない間接参照を追加しても何の価値もありません。それは価値のない抽象化をすることで、コードを読み、理解し、推論することをさらに困難にします。インタフェースを追加する明確な理由がなく、インタフェースによってコードがどのように改善されるかが不明瞭な場合は、そのインタフェースの目的に異議を唱える必要があります。実装を直接呼び出すのも一つの手です。

+

コード内で抽象化するときは注意が必要です(抽象化は作成するのではなく、発見する必要があります)。後で必要になる可能性があるものを考慮し、完璧な抽象化レベルを推測して、私たちソフトウェア開発者はコードをオーバーエンジニアリングすることがよくあります。ほとんどの場合、コードが不必要な抽象化で汚染され、読みにくくなるため、このプロセスは避けるべきです。

+
+

ロブ・パイク

+

インタフェースでデザインするな。インタフェースを見つけ出せ。

+
+

抽象的に問題を解決しようとするのではなく、今解決すべきことを解決しましょう。最後に重要なことですが、インタフェースによってコードがどのように改善されるかが不明瞭な場合は、コードを簡素化するためにインタフェースを削除することを検討する必要があるでしょう。

+

ソースコード

+

生産者側のインタフェース (#6)

+
+要約 +

インタフェースをクライアント側で保持することで不必要な抽象化を回避できます。

+
+

Go言語ではインタフェースが暗黙的に満たされます。これは、明示的な実装を持つ言語と比較して大きな変化をもたらす傾向があります。ほとんどの場合、従うべきアプローチは前のセクションで説明したもの――抽象化は作成するのではなく、発見する必要がある――に似ています。これは、すべてのクライアントに対して特定の抽象化を強制するのは生産者の役割ではないことを意味します。代わりに、何らかの形式の抽象化が必要かどうかを判断し、そのニーズに最適な抽象化レベルを決定するのはクライアントの責任です。

+

ほとんどの場合、インタフェースは消費者側に存在する必要があります。ただし、特定の状況(たとえば、抽象化が消費者にとって役立つことがわかっている――予測はしていない――場合)では、それを生産者側で使用したい場合があります。そうした場合、可能な限り最小限に抑え、再利用可能性を高め、より簡単に構成できるように努めるべきです。

+

ソースコード

+

インタフェースを返す (#7)

+
+要約 +

柔軟性に問題がないようにするために、関数はほとんどの場合、インタフェースではなく具体的​​な実装を返す必要があります。逆に、関数は可能な限りインタフェースを受け入れる必要があります。

+
+

ほとんどの場合、インタフェースではなく具体的な実装を返す必要があります。そうでないとパッケージの依存関係により設計がいっそう複雑になり、すべてのクライアントが同じ抽象化に依存する必要があるため、柔軟性に欠ける可能性があります。結論は前のセクションと似ています。抽象化がクライアントにとって役立つことが(予測されるではなく)わかっている場合は、インタフェースを返すことを検討してもよいでしょう。それ以外の場合は、抽象化を強制すべきではありません。それらはクライアントによって発見される必要があります。何らかの理由でクライアントが実装を抽象化する必要がある場合でも、クライアント側でそれを行うことができます。

+

any は何も言わない (#8)

+
+要約 +

json.Marshal など考えうるすべての型を受け入れるか返す必要がある場合にのみ any を使用してください。それ以外の場合、any は意味のある情報を提供せず、呼び出し元が任意のデータ型のメソッドを呼び出すことを許可するため、コンパイル時に問題が発生する可能性があります。

+
+

any 型は、考えうるすべての型を受け入れるか返す必要がある場合(たとえば、マーシャリングやフォーマットの場合)に役立ちます。原則としてコードを過度に一般化することは何としても避けるべきです。コードの表現力などの他の側面が向上する場合は、コードを少し重複させたほうが良いこともあります。

+

ソースコード

+

ジェネリックスをいつ使用するべきか理解していない (#9)

+
+要約 +

ジェネリックスと型パラメーターを利用することで、要素や動作を除外するためのボイラープレートコードを避けることができます。ただし、型パラメータは時期尚早に使用せず、具体的な必要性がわかった場合にのみ使用してください。そうでなければ、不必要な抽象化と複雑さが生じます。

+
+

セクション全文はこちら

+

ソースコード

+

型の埋め込みで起こりうる問題を把握していない (#10)

+
+要約 +

型埋め込みを使用すると、ボイラープレートコードを回避することもできます。ただし、そうすることで、一部のフィールドを非表示にしておく必要がある場合に問題が発生しないようにしてください。

+
+

構造体を作成するとき、Go言語は型を埋め込むオプションを提供します。ただし、型埋め込みの意味をすべて理解していないと、予想外の動作が発生する可能性があります。このセクションでは、型を埋め込む方法、それがもたらすもの、および考えられる問題について見ていきます。

+

Go言語では、名前なしで宣言された構造体フィールドは、埋め込みと呼ばれます。たとえば、次のようなものです。

+
type Foo struct {
+    Bar // 埋め込みフィールド
+}
+
+type Bar struct {
+    Baz int
+}
+
+

Foo 構造体では、Bar 型が関連付けられた名前なしで宣言されています。したがって、これは埋め込みフィールドです。

+

埋め込みを使用することで、埋め込み型のフィールドとメソッドは昇格します。Bar には Baz フィールドが含まれているため、このフィールドは Foo に昇格します。したがって、Foo から Baz を利用できるようになります。

+

型の埋め込みについて何が言えるでしょうか。まず、これが必要になることはほとんどなく、ユースケースが何であれ、おそらく型埋め込みなしでも同様に解決できることを意味します。型の埋め込みは主に利便性を目的として使用されます。ほとんどの場合、それは動作を昇格するために使用されます。

+

型埋め込みを使用する場合は、次の 2 つの主な制約を念頭に置く必要があります。

+
    +
  • フィールドへのアクセスを簡素化するための糖衣構文としてのみ使用しないでください( Foo.Bar.Baz() の代わりに Foo.Baz() など)。 これが唯一の根拠である場合は、内部型を埋め込まず、代わりにフィールドを使いましょう。
  • +
  • 外部から隠したいデータ(フィールド)や動作(メソッド)を昇格してはなりません。たとえば、構造体に対してプライベートなままにしておく必要があるロック動作にクライアントがアクセスできるようにする場合などです。
  • +
+

これらの制約を念頭に置いて型埋め込みを意識的に使用すると、追加の転送メソッドによるボイラープレートコードを回避するのに役立ちます。ただし、見た目だけを目的としたり、隠すべき要素を昇格したりしないように注意しましょう。

+

ソースコード

+

Functional Options パターンを使用していない (#11)

+
+要約 +

API に適した方法でオプションを便利に処理するには、Functional Options パターンを使用しましょう。

+
+

さまざまな実装方法が存在し、多少の違いはありますが、主な考え方は次のとおりです。

+
    +
  • 未エクスポートの構造体はオプション設定を保持します。
  • +
  • 各オプションは同じ型、type Option func(options *options) エラーを返す関数です。たとえば、WithPort はポートを表す int 引数を受け取り、options 構造体の更新方法を表す Option 型を返します。
  • +
+

+
type options struct {
+  port *int
+}
+
+type Option func(options *options) error
+
+func WithPort(port int) Option {
+  return func(options *options) error {
+    if port < 0 {
+    return errors.New("port should be positive")
+  }
+  options.port = &port
+  return nil
+  }
+}
+
+func NewServer(addr string, opts ...Option) ( *http.Server, error) {
+  var options options
+  for _, opt := range opts { 
+    err := opt(&options) 
+    if err != nil {
+      return nil, err
+    }
+  }
+
+// この段階で、options 構造体が構築され、構成が含まれます。
+// したがって、ポート設定に関連するロジックを実装できます。
+  var port int
+  if options.port == nil {
+    port = defaultHTTPPort
+  } else {
+      if *options.port == 0 {
+      port = randomPort()
+    } else {
+      port = *options.port
+    }
+  }
+
+  // ...
+}
+
+

Functional Options パターンは、オプションを処理するための手軽で API フレンドリーな方法を提供します。 Builder パターンは有効なオプションですが、いくつかの小さな欠点(空の可能性がある構成構造体を渡さなければならない、またはエラーを処理する方法があまり便利ではない)があり、この種の問題において Functional Options パターンがGo言語における慣用的な対処方法になる傾向があります。

+

ソースコード

+

誤ったプロジェクト構成 (プロジェクト構造とパッケージ構成) (#12)

+

全体的な構成に関しては、さまざまな考え方があります。たとえば、アプリケーションをコンテキストごとに整理すべきか、それともレイヤーごとに整理すべきか、それは好みによって異なります。コンテキスト(顧客コンテキスト、契約コンテキストなど)ごとにコードをグループ化することを選ぶ場合もあれば、六角形のアーキテクチャ原則に従うことと、技術層ごとにグループ化することを選ぶ場合もあります。私たちが行う決定が一貫している限り、それがユースケースに適合するなら、それが間違っていることはありません。

+

パッケージに関しては、従うべきベストプラクティスが複数あります。まず、プロジェクトが過度に複雑になる可能性があるため、時期尚早なパッケージ化は避けるべきです。場合によっては、完璧な構造を最初から無理に作ろうとするよりも、単純な構成を使用し、その内容を理解した上でプロジェクトを発展させるほうが良い場合があります。 +粒度も考慮すべき重要な点です。 1 つまたは 2 つのファイルだけを含む数十のナノパッケージを作成することは避けるべきです。その場合、おそらくこれらのパッケージ間の論理的な接続の一部が抜け落ち、読み手にとってプロジェクトが理解しにくくなるからです。逆に、パッケージ名の意味を薄めるような巨大なパッケージも避けるべきです。

+

パッケージの名前付けも注意して行う必要があります。(開発者なら)誰もが知っているように、名前を付けるのは難しいです。クライアントが Go プロジェクトを理解しやすいように、パッケージに含まれるものではなく、提供するものに基づいてパッケージに名前を付ける必要があります。また、ネーミングには意味のあるものを付ける必要があります。したがって、パッケージ名は短く、簡潔で、表現力豊かで、慣例により単一の小文字にする必要があります。

+

何をエクスポートするかについてのルールは非常に簡単です。パッケージ間の結合を減らし、エクスポートされる不要な要素を非表示にするために、エクスポートする必要があるものをできる限り最小限に抑える必要があります。要素をエクスポートするかどうか不明な場合は、デフォルトでエクスポートしないようにする必要があります。後でエクスポートする必要があることが判明した場合は、コードを調整できます。また、構造体を encoding/json でアンマーシャリングできるようにフィールドをエクスポートするなど、いくつかの例外にも留意してください。

+

プロジェクトを構成するのは簡単ではありませんが、これらのルールに従うことで維持が容易になります。ただし、保守性を容易にするためには一貫性も重要であることに注意してください。したがって、コードベース内で可能な限り一貫性を保つようにしましょう。

+
+補足 +

Go チームは Go プロジェクトの組織化/構造化に関する公式ガイドラインを 2023 年に発行しました: go.dev/doc/modules/layout

+
+

ユーティリティパッケージの作成 (#13)

+
+要約 +

名前付けはアプリケーション設計の重要な部分です。commonutilshared のようなパッケージを作成しても、読み手にそれほどの価値をもたらしません。このようなパッケージを意味のある具体的なパッケージ名にリファクタリングしましょう。

+
+

また、パッケージに含まれるものではなく、パッケージが提供するものに基づいてパッケージに名前を付けると、その表現力を高める効率的な方法になることにも留意してください。

+

ソースコード

+

パッケージ名の衝突を無視する (#14)

+
+要約 +

混乱、さらにはバグにつながりかねない、変数とパッケージ間の名前の衝突を回避するために、それぞれに一意の名前を使用しましょう。これが不可能な場合は、インポートエイリアスを使用して修飾子を変更してパッケージ名と変数名を区別するか、より良い名前を考えてください。

+
+

パッケージの衝突は、変数名が既存のパッケージ名と衝突する場合に発生し、パッケージの再利用が妨げられます。曖昧さを避けるために、変数名の衝突を防ぐ必要があります。衝突が発生した場合は、別の意味のある名前を見つけるか、インポートエイリアスを使用する必要があります。

+

コードの文章化が行われていない (#15)

+
+要約 +

クライアントとメンテナがコードの意図を理解できるように、エクスポートされた要素を文章化しましょう。

+
+

文章化はコーディングの重要な側面です。これにより、クライアントが API をより簡単に使用することができますが、プロジェクトの維持にも役立ちます。Go言語では、コードを慣用的なものにするために、いくつかのルールに従う必要があります。

+

まず、エクスポートされたすべての要素を文章化する必要があります。構造、インタフェース、関数など、エクスポートする場合は文章化する必要があります。慣例として、エクスポートされた要素の名前から始まるコメントを追加します。

+

慣例として、各コメントは句読点で終わる完全な文である必要があります。また、関数(またはメソッド)を文章化するときは、関数がどのように実行するかではなく、その関数が何を実行するつもりであるかを強調する必要があることにも留意してください。これはドキュメントではなく、関数とコメントについてです。ドキュメントは理想的には、利用者がエクスポートされた要素の使用方法を理解するためにコードを見る必要がないほど十分な情報を提供する必要があります。

+

変数または定数を文章化する場合、その目的と内容という 2 つの側面を伝えることが重要かもしれません。前者は、外部クライアントにとって役立つように、コードドキュメントとして存在する必要があります。ただし、後者は必ずしも公開されるべきではありません。

+

クライアントとメンテナがパッケージの目的を理解できるように、各パッケージをドキュメントする必要もあります。慣例として、コメントは //Package で始まり、その後にパッケージ名が続きます。パッケージコメントの最初の行は、パッケージに表示されるため簡潔にする必要があります。そして、次の行に必要な情報をすべて入力します。

+

コードを文章化することが制約になるべきではありません。クライアントやメンテナがコードの意図を理解するのに役立つ必要があります。

+

リンターを使用してない (#16)

+
+要約 +

コードの品質と一貫性を向上させるには、リンターとフォーマッターを使用しましょう

+
+

リンターは、コードを分析してエラーを検出する自動ツールです。このセクションの目的は、既存のリンターの完全なリストを提供することではありません。そうした場合、すぐに使い物にならなくなってしまうからです。ただし、ほとんどの Go プロジェクトにリンターが不可欠であるということは理解し、覚えておきましょう。

+ +

リンターのほかに、コードスタイルを修正するためにコードフォーマッターも使用しましょう。以下に、いくつかのコードフォーマッターを示します。

+ +

ほかに golangci-lint (https://github.com/golangci/golangci-lint) というものがあります。これは、多くの便利なリンターやフォーマッターの上にファサードを提供するリンティングツールです。また、リンターを並列実行して分析速度を向上させることができ、非常に便利です。

+

リンターとフォーマッターは、コードベースの品質と一貫性を向上させる強力な方法です。時間をかけてどれを使用すべきかを理解し、それらの実行( CI や Git プリコミットフックなど)を自動化しましょう。

+

データ型

+

8 進数リテラルで混乱を招いてしまう (#17)

+
+要約 +

既存のコードを読むときは、 0 で始まる整数リテラルが 8 進数であることに留意してください。また、接頭辞 0o を付けることで8進数であることを明確にし、読みやすさを向上させましょう。

+
+

8 進数は 0 で始まります(たとえば、010 は 10 進数の 8 に相当します)。可読性を向上させ、将来のコードリーダーの潜在的な間違いを回避するには、 0o 接頭辞を使用して 8 進数であることを明らかにしましょう(例: 0o10 )。

+

他の整数リテラル表現にも注意してください。

+
    +
  • バイナリ - 接頭辞 0b あるいは 0B を使用します(たとえば、 0b は 10 進数の 4 に相当します)
  • +
  • 16進数 - 接頭辞 0x あるいは 0X を使用します(たとえば、 0xF は 10 進数の 15 に相当します)。
  • +
  • 虚数 - 接尾辞 i を使用します(たとえば、 3i
  • +
+

読みやすくするために、区切り文字としてアンダースコア( _ )を使用することもできます。たとえば、 10 億は 1_000_000_000 のように書くことができます。アンダースコアは 0b)00_00_01 のように他の表現と併用することもできます。

+

ソースコード

+

整数オーバーフローを無視している (#18)

+
+要約 +

Go言語では整数のオーバーフローとアンダーフローが裏側で処理されるため、それらをキャッチする独自の関数を実装できます。

+
+

Go言語では、コンパイル時に検出できる整数オーバーフローによってコンパイルエラーが生成されます。たとえば、次のようになります。

+
var counter int32 = math.MaxInt32 + 1
+
+
constant 2147483648 overflows int32
+
+

ただし、実行時には、整数のオーバーフローまたはアンダーフローは発生しません。これによってアプリケーションのパニックが発生することはありません。この動作はやっかいなバグ(たとえば、負の結果につながる整数の増分や正の整数の加算など)につながる可能性があるため、頭に入れておくことが重要です。

+

ソースコード

+

浮動小数点を理解していない (#19)

+
+要約 +

特定のデルタ内で浮動小数点比較を行うと、コードの移植性を確保できます。加算または減算を実行するときは、精度を向上させるために、同程度の大きさの演算をグループ化してください。また、乗算と除算は加算と減算の前に実行してください。

+
+

Go言語には、(虚数を除いた場合) float32float64 という 2 つの浮動小数点型があります。浮動小数点の概念は、小数値を表現できないという整数の大きな問題を解決するために発明されました。予想外の事態を避けるために、浮動小数点演算は実際の演算の近似であることを知っておく必要があります。

+

そのために、乗算の例を見てみましょう。

+
var n float32 = 1.0001
+fmt.Println(n * n)
+
+

このコードにおいては 1.0001 * 1.0001 = 1.00020001 という結果が出力されることを期待すると思います。しかしながら、ほとんどの x86 プロセッサでは、代わりに 1.0002 が出力されます。

+

Go言語の float32 および float64 型は近似値であるため、いくつかのルールを念頭に置く必要があります。

+
    +
  • 2 つの浮動小数点数を比較する場合は、その差が許容範囲内であることを確認する。
  • +
  • 加算または減算を実行する場合、精度を高めるために、同じ桁数の演算をグループ化する。
  • +
  • 精度を高めるため、一連の演算で加算、減算、乗算、除算が必要な場合は、乗算と除算を最初に実行する。
  • +
+

ソースコード

+

スライスの長さと容量を理解していない (#20)

+
+要約 +

Go 開発者ならば、スライスの長さと容量の違いを理解するべきです。スライスの長さはスライス内の使用可能な要素の数であり、スライスの容量はバッキング配列内の要素の数です。

+
+

セクション全文はこちら

+

ソースコード

+

非効率なスライスの初期化 (#21)

+
+要約 +

スライスを作成するとき、長さがすでにわかっている場合は、指定された長さまたは容量でスライスを初期化しましょう。これにより、割り当ての数が減り、パフォーマンスが向上します。

+
+

make を使用してスライスを初期化するときに、長さとオプションの容量を指定できます。これらのパラメータの両方に適切な値を渡すことが適当であるにもかかわらず、それを忘れるのはよくある間違いです。実際、複数のコピーが必要になり、一時的なバッキング配列をクリーンアップするために GC に追加の労力がかかる可能性があります。パフォーマンスの観点から言えば、Go ランタイムに手を差し伸べない理由はありません。

+

オプションは、指定された容量または指定された長さのスライスを割り当てることです。 これら 2 つの解決策のうち、2 番目の解決策の方がわずかに高速である傾向があることがわかりました。ただし、特定の容量と追加を使用すると、場合によっては実装と読み取りが容易になることがあります。

+

ソースコード

+

nil と空のスライスを混同している (#22)

+
+要約 +

encoding/jsonreflect パッケージなどを使用するときによくある混乱を避けるためには、nil スライスと空のスライスの違いを理解する必要があります。どちらも長さゼロ、容量ゼロのスライスですが、割り当てを必要としないのは nil スライスだけです。

+
+

Go言語では、nil と空のスライスは区別されます。nil スライスは nil に等しいのに対し、空のスライスの長さはゼロです。nil スライスは空ですが、空のスライスは必ずしもnil であるとは限りません。一方、nil スライスには割り当ては必要ありません。このセクション全体を通して、以下の方法を使用することによって、状況に応じてスライスを初期化することを見てきました。

+
    +
  • 最終的な長さが不明でスライスが空の場合は var s []string
  • +
  • nil と空のスライスを作成する糖衣構文としての []string(nil)
  • +
  • 将来の長さがわかっている場合は make([]string, length)
  • +
+

要素なしでスライスを初期化する場合、最後のオプション []string{} は避けるべきです。最後に、予想外の動作を防ぐために、使用するライブラリが nil と空のスライスを区別しているかどうかを確認してみましょう。

+

ソースコード

+

スライスが空かどうかを適切に確認しない (#23)

+
+要約 +

スライスに要素が含まれていないことを確認するには、その長さを確認しましょう。これは、スライスが nil であるか空であるかに関係なく機能します。マップについても同様です。明確な API を設計するには、nil スライスと空のスライスを区別しないでください。

+
+

スライスに要素があるかどうかを判断するには、スライスが nil かどうか、またはその長さが 0 に等しいかどうかを確認することで判断できます。スライスが空である場合とスライスが nil である場合の両方をカバーできるため、長さを確かめることが最良の方法です。

+

一方、インタフェースを設計するときは、軽微なプログラミングエラーを起こさないよう nil スライスと空のスライスを区別しないようにする必要があります。スライスを返すときに、nil または空のスライスを返すかどうかは、意味的にも技術的にも違いはありません。コーラーにとってはどちらも同じことを意味するはずです。この原理はマップでも同じです。マップが空かどうかを確認するには、それが nil かどうかではなく、その長さを確認しましょう。

+

ソースコード

+

スライスのコピーを正しく作成していない (#24)

+
+要約 +

組み込み関数 copy を使用してあるスライスを別のスライスにコピーするには、コピーされる要素の数が 2 つのスライスの長さの間の最小値に相当することに注意してください。

+
+

要素をあるスライスから別のスライスにコピーする操作は、かなり頻繁に行われます。コピーを使用する場合、コピー先にコピーされる要素の数は 2 つのスライスの長さの間の最小値に相当することに注意する必要があります。また、スライスをコピーするための他の代替手段が存在することにも留意してください。そのため、コードベースでそれらを見つけても驚くことはありません。

+

ソースコード

+

append の使用による予想外の副作用 (#25)

+
+要約 +

2つの異なる関数が同じ配列に基づくスライスを使用する場合に、copy または完全スライス式を使用することで append による衝突を防ぐことができます。ただし、大きなスライスを縮小する場合、メモリリークを防ぐことができるのはスライスのコピーだけです。

+
+

スライスを使用するときは、予想外の副作用につながる状況に直面する可能性があることを覚えておく必要があります。結果のスライスの長さがその容量より小さい場合、追加によって元のスライスが変更される可能性があります。起こり得る副作用の範囲を制限したい場合は、スライスのコピーまたは完全スライス式を使用できます。これにより、コピーを実行できなくなります。

+
+補足 +

s[low:high:max](完全スライス式)――この命令文は、容量が max - low に等しいことを除けば、s[low:high] で作成されたスライスと同様のスライスを作成します。

+
+

ソースコード

+

スライスとメモリリーク (#26)

+
+要約 +

ポインタのスライスまたはポインタフィールドを持つ構造体を操作する場合、スライス操作によって除外された要素を nil とすることでメモリリークを回避できます。

+
+

容量漏れ

+

大きなスライスまたは配列をスライスすると、メモリ消費が高くなる可能性があることに注意してください。残りのスペースは GC によって再利用されず、少数の要素しか使用しないにもかかわらず、大きなバッキング配列が保持されます。スライスのコピーをすることで、このような事態を防ぐことができます。

+

ソースコード

+

スライスとポインタ

+

ポインタまたはポインタフィールドを含む構造体を使用してスライス操作をする場合、GC がこれらの要素を再利用しないことを知っておく必要があります。その場合の選択肢は、コピーを実行するか、残りの要素またはそのフィールドを明示的に nil とすることです。

+

ソースコード

+

非効率なマップの初期化 (#27)

+
+要約 +

マップを作成するとき、その長さがすでにわかっている場合は、指定された長さで初期化します。これにより、割り当ての数が減り、パフォーマンスが向上します。

+
+

マップは、キー・値ペアの順序なしコレクションを提供します。なお、それぞれのペアは固有のキーを持ちます。Go言語では、マップはハッシュテーブルデータ構造に基づいています。内部的には、ハッシュテーブルはバケットの配列であり、各バケットはキー・値ペアの配列へのポインタです。

+

マップに含まれる要素の数が事前にわかっている場合は、その初期サイズを指定して作成する必要があります。マップの増大は、十分なスペースを再割り当てし、すべての要素のバランスを再調整する必要があるため、計算量が非常に多くなりますが、これによりそれを回避することができます。

+

ソースコード

+

マップとメモリリーク (#28)

+
+要約 +

マップはメモリ内で常に増大する可能性がありますが、縮小することはありません。したがって、メモリの問題が発生する場合は、マップを強制的に再生成したり、ポインタを使用したりするなど、さまざまな手段を試すことができます。

+
+

セクション全文はこちら

+

ソースコード

+

誤った方法による値の比較 (#29)

+
+要約 +

Go言語で型を比較す​​るには、2 つの型が比較可能ならば、== 演算子と != 演算子を使用できます。真偽値、数値、文字列、ポインタ、チャネル、および構造体が完全に比較可能な型で構成されています。それ以外は、 reflect.DeepEqual を使用してリフレクションの代償を支払うか、独自の実装とライブラリを使用することができます。

+
+

効果的に比較するには、 ==!= の使用方法を理解することが不可欠です。これらの演算子は、比較可能な被演算子で使用できます。

+
    +
  • 真偽値 - 2 つの真偽値が等しいかどうかを比較します。
  • +
  • 数値 (int、float、および complex 型) - 2 つの数値が等しいかどうかを比較します。
  • +
  • 文字列 - 2 つの文字列が等しいかどうかを比較します。
  • +
  • チャネル - 2 つのチャネルが同じ make 呼び出しによって作成されたか、または両方が nil であるかを比較します。
  • +
  • インタフェース - 2 つのインタフェースが同じ動的タイプと等しい動的値を持つかどうか、または両方が nil であるかどうかを比較します。
  • +
  • ポインタ - 2 つのポインタがメモリ内の同じ値を指しているか、または両方とも nil であるかを比較します。
  • +
  • 構造体と配列 - 類似した型で構成されているかどうかを比較します。
  • +
+
+補足 +

?>=< 、および > 演算子を数値型で使用して値を比較したり、文字列で字句順序を比較したりすることもできます。

+
+

被演算子が比較できない場合(スライスとマップなど)、リフレクションなどの他の方法を利用する必要があります。リフレクションはメタプログラミングの一種であり、アプリケーションがその構造と動作を内省して変更する機能を指します。たとえば、Go言語では reflect.DeepEqual を使用できます。この関数は、2つの値を再帰的に調べることによって、2つの要素が完全に等しいかどうかを報告します。受け入れられる要素は、基本型に加えて、配列、構造体、スライス、マップ、ポインタ、インタフェース、関数です。しかし、最大の落とし穴はパフォーマンス上のペナルティです。

+

実行時のパフォーマンスが重要な場合は、独自のメソッドを実装することが最善となる可能性があります。

+

追記:標準ライブラリには既に比較メソッドがいくつかあることを覚えておく必要があります。たとえば、最適化された bytes.Compare 関数を使用して、2つのバイトスライスを比較できます。独自のメソッドを実装する前に、車輪の再発明をしないようにしましょう。

+

ソースコード

+

制御構造

+

要素が range ループ内でコピーされることを知らない (#30)

+
+要約 +

range ループ内の value 要素はコピーです。したがって、たとえば構造体を変更するには、そのインデックスを介してアクセスするか、従来の for ループを介してアクセスしましょう(変更する要素またはフィールドがポインタである場合を除く)。

+
+

range ループを使用すると、さまざまなデータ構造に反復処理を行うことができます。

+
    +
  • 文字列
  • +
  • 配列
  • +
  • 配列へのポインタ
  • +
  • スライス
  • +
  • マップ
  • +
  • 受信チャネル
  • +
+

古典的な for ループと比較すると、range ループはその簡潔な構文のおかげで、これらのデータ構造のすべての要素に反復処理をするのに便利です。

+

ただし、range ループ内の値要素はコピーであることを覚えておく必要があります。したがって、値を変更する必要がある構造体の場合、変更する値またはフィールドがポインタでない限り、要素自体ではなくコピーのみを更新します。range ループまたは従来の for ループを使用してインデックス経由で要素にアクセスすることが推奨されます。

+

ソースコード

+

range ループ(チャネルと配列)での引数の評価方法を知らない (#31)

+
+要約 +

range 演算子に渡される式はループの開始前に 1 回だけ評価されることを理解すると、チャネルまたはスライスの反復処理における非効率な割り当てなどのありがちな間違いを回避できます。

+
+

range ループは、(タイプに関係なく)コピーを実行することにより、ループの開始前に、指定された式を 1 回だけ評価します。たとえば、誤った要素にアクセスしてしまう、というようなありがちな間違いを避けるために、この動作を覚えておく必要があります。たとえば

+
a := [3]int{0, 1, 2}
+for i, v := range a {
+    a[2] = 10
+    if i == 2 {
+        fmt.Println(v)
+    }
+}
+
+

このコードは、最後のインデックスを 10 に更新します。しかし、このコードを実行すると、10 は出力されません。 2 が出力されます。

+

ソースコード

+

range ループ内におけるポインタ要素の使用が及ぼす影響を分かっていない (#32)

+
+要約 +

ローカル変数を使用するか、インデックスを使用して要素にアクセスすると、ループ内でポインタをコピーする際の間違いを防ぐことができます。

+
+

range ループを使用してデータ構造に反復処理を施す場合、すべての値が単一の一意のアドレスを持つ一意の変数に割り当てられることを思い出してください。ゆえに、各反復処理中にこの変数を参照するポインタを保存すると、同じ要素、つまり最新の要素を参照する同じポインタを保存することになります。この問題は、ループのスコープ内にローカル変数を強制的に作成するか、インデックスを介してスライス要素を参照するポインタを作成することで解決できます。どちらの解決策でも問題ありません。

+

ソースコード

+

マップの反復処理中に誤った仮定をする(反復処理中の順序付けとマップの挿入) (#33)

+
+要約 +

マップを使用するときに予測可能な出力を保証するには、マップのデータ構造が次のとおりであることに注意してください。

+
+
    +
  • データをキーで並べ替えません
  • +
  • 挿入順序は保持されません
  • +
  • 反復処理順序は決まっていません
  • +
  • ある反復処理中に追加された要素がその処理中に生成されることを保証しません
  • +
+ + +

ソースコード

+

break 文がどのように機能するかを分かっていない (#34)

+
+要約 +

ラベルと break または continue の併用は、特定の命令文を強制的に中断します。これは、ループ内の switch または select 文で役立ちます。

+
+

通常、break 文はループの実行を終了するために使用されます。ループが switch または select と組み合わせて使用​​される場合、目的の命令文ではないのに中断させてしまう、というミスをすることが開発者にはよくあります。たとえば

+
for i := 0; i < 5; i++ {
+    fmt.Printf("%d ", i)
+
+    switch i {
+    default:
+    case 2:
+        break
+    }
+}
+
+

break 文は for ループを終了させるのではなく、代わりに switch 文を終了させます。したがって、このコードは 0 から 2 までを反復する代わりに、0 から 4 までを反復します(0 1 2 3 4)。

+

覚えておくべき重要なルールの1つは、 break 文は最も内側の forswitch 、または select 文の実行を終了するということです。前の例では、switch 文を終了します。

+

switch 文の代わりにループを中断する最も慣用的な方法はラベルを使用することです。

+
loop:
+    for i := 0; i < 5; i++ {
+        fmt.Printf("%d ", i)
+
+        switch i {
+        default:
+        case 2:
+            break loop
+        }
+    }
+
+

ここでは、loop ラベルを for ループに関連付けます。 次に、break 文に loop ラベルを指定するので、switch ではなく loop が中断されます。よって、この新しいバージョンは予想どおり 0 1 2 を出力します。

+

ソースコード

+

ループ内で defer を使用する (#35)

+
+要約 +

関数内のループロジックの抽出は、各反復の最後での defer 文の実行につながります。

+
+

defer 文は、上位ブロックの関数が戻るまで呼び出しの実行を遅らせます。これは主に定型コードを削減するために使用されます。たとえば、リソースを最終的に閉じる必要がある場合は、defer を使用して、return を実行する前にクロージャ呼び出しを繰り返すことを避けることができます。

+

defer でよくあるミスの1つは、上位ブロック の関数が戻ったときに関数呼び出しがスケジュールされることを忘れることです。たとえば

+
func readFiles(ch <-chan string) error {
+    for path := range ch {
+        file, err := os.Open(path)
+        if err != nil {
+            return err
+        }
+
+        defer file.Close()
+
+        // ファイルの処理をする
+    }
+    return nil
+}
+
+

defer 呼び出しは、各ループ反復中ではなく、readFiles 関数が返されたときに実行されます。 readFiles が返らない場合、ファイル記述子は永久に開いたままになり、リークが発生します。

+

この問題を解決するための一般的な手段の1つは、 defer の後に、各反復中に呼び出される上位ブロックの関数を作成することです。

+
func readFiles(ch <-chan string) error {
+    for path := range ch {
+        if err := readFile(path); err != nil {
+            return err
+        }
+    }
+    return nil
+}
+
+func readFile(path string) error {
+    file, err := os.Open(path)
+    if err != nil {
+        return err
+    }
+
+    defer file.Close()
+
+    // ファイルの処理をする
+    return nil
+}
+
+

別の解決策は、readFile 関数をクロージャにすることですが、本質的には同じです。別の上位ブロックの関数を追加して、各反復中に defer 呼び出しを実行します。

+

ソースコード

+

文字列

+

ルーンを理解していない (#36)

+
+要約 +

ルーンが Unicode コードポイントの概念に対応し、複数のバイトで構成される可能性があることを理解することは、 Go 開発者が文字列を正確に操作するために不可欠です。

+
+

Go言語ではルーンがあらゆる場所に使用されるため、次の点を理解することが重要です。

+
    +
  • 文字セットは文字の集合ですが、エンコーディングは文字セットをバイナリに変換する方法を記述します。
  • +
  • Go言語では、文字列は任意のバイトの不変スライスを参照します。
  • +
  • Go言語のソースコードは UTF-8 でエンコードされています。したがって、すべの文字列リテラルは UTF-8 文字列です。ただし、文字列には任意のバイトが含まれる可能性があるため、文字列が(ソースコードではない)他の場所から取得された場合、その文字列が UTF-8 エンコーディングに基づいている保証はありません。
  • +
  • rune は Unicode コードポイントの概念に対応し、単一の値で表されるアイテムを意味します。
  • +
  • UTF-8 を使用すると、Unicode コードポイントを 1 ~ 4 バイトにエンコードできます。
  • +
  • Go言語で文字列に対して len() を使用すると、ルーン数ではなくバイト数が返されます。
  • +
+

ソースコード

+

文字列に対する不正な反復処理 (#37)

+
+要約 +

range 演算子を使用して文字列を反復処理すると、ルーンのバイトシーケンスの開始インデックスに対応するインデックスを使用してルーンが反復処理されます。特定のルーンインデックス( 3 番目のルーンなど)にアクセスするには、文字列を []rune に変換します。

+
+

文字列の反復処理は、開発者にとって一般的な操作です。おそらく、文字列内の各ルーンに対して操作を実行するか、特定の部分文字列を検索する独自の関数を実装する必要があるでしょう。どちらの場合も、文字列の異なるルーンを反復処理する必要があります。しかし、反復処理がどのように機能するかについては困惑しやすいです。

+

次の例を考えてみましょう。

+
s := "hêllo"
+for i := range s {
+    fmt.Printf("position %d: %c\n", i, s[i])
+}
+fmt.Printf("len=%d\n", len(s))
+
+
position 0: h
+position 1: Ã
+position 3: l
+position 4: l
+position 5: o
+len=6
+
+

混乱を招く可能性のある 3 点を取り上げましょう。

+
    +
  • 2 番目のルーンは、出力では ê ではなく Ã になります。
  • +
  • position 1 から position 3 にジャンプしました。 position 2 には何があるのでしょうか。
  • +
  • len は 6 を返しますが、s には 5 つのルーンしか含まれていません。
  • +
+

結果の最後から見ていきましょう。len はルーン数ではなく、文字列内のバイト数を返すことはすでに述べました。文字列リテラルを s に割り当てているため、s は UTF-8 文字列です。一方、特殊文字「ê」は 1 バイトでエンコードされません。 2 バイト必要です。したがって、len(s) を呼び出すと 6 が返されます。

+

前の例では、各ルーンを反復処理していないことを理解する必要があります。代わりに、ルーンの各開始インデックスを反復処理します。

+

+

s[i] を出力しても i 番目のルーンは出力されません。インデックス i のバイトの UTF-8 表現を出力します。したがって、 hêllo の代わりに hÃllo を出力がされます。

+

さまざまなルーン文字をすべて出力したい場合は、 range 演算子の value 要素を使用することができます。

+
s := "hêllo"
+for i, r := range s {
+    fmt.Printf("position %d: %c\n", i, r)
+}
+
+

または、文字列をルーンのスライスに変換し、それを反復処理することもできます。

+
s := "hêllo"
+runes := []rune(s)
+for i, r := range runes {
+    fmt.Printf("position %d: %c\n", i, r)
+}
+
+

この解決策では、以前の解決策と比較して実行時のオーバーヘッドが発生することに注意してください。実際、文字列をルーンのスライスに変換するには、追加のスライスを割り当て、バイトをルーンに変換する必要があります。文字列のバイト数を n とすると、時間計算量は O(n) になります。したがって、すべてのルーンを反復処理する場合は、最初の解決策を使用するべきです。

+

ただし、最初の方法を使用して文字列の i 番目のルーンにアクセスしたい場合は、ルーンインデックスにアクセスできません。代わりに、バイトシーケンス内のルーンの開始インデックスがわかります。

+
s := "hêllo"
+r := []rune(s)[4]
+fmt.Printf("%c\n", r) // o
+
+

ソースコード

+

trim 関数の誤用 (#38)

+
+要約 +

strings.TrimRightstrings.TrimLeft は、指定されたセットに含まれるすべての末尾・先頭のルーンを削除しますが、 strings.TrimSuffixstrings.TrimPrefix は、指定された接尾辞・接頭辞のない文字列を返します。

+
+

たとえば

+
fmt.Println(strings.TrimRight("123oxo", "xo"))
+
+

は 123 を出力します

+

+

逆に、 strings.TrimLeft は、セットに含まれる先頭のルーンをすべて削除します。

+

一方、strings.TrimSuffixstrings.TrimPrefix は、指定された末尾の接尾辞・接頭辞を除いた文字列を返します。

+

ソースコード

+

最適化が不十分な文字列の連結 (#39)

+
+要約 +

文字列のリストの連結は、反復ごとに新しい文字列が割り当てられないように、strings.Builder を使用して行う必要があります。

+
+

+= 演算子を用いてスライスのすべての文字列要素を連結する concat 関数を考えてみましょう。

+
func concat(values []string) string {
+    s := ""
+    for _, value := range values {
+        s += value
+    }
+    return s
+}
+
+

各反復中に、 += 演算子は s と value 文字列を連結します。一見すると、この関数は間違っていないように見えるかもしれません。しかし、この実装は、文字列の核となる特性の1つである不変性を忘れています。したがって、各反復では s は更新されません。メモリ内に新しい文字列を再割り当てするため、この関数のパフォーマンスに大きな影響を与えます。

+

幸いなことに、 strings.Builder を用いることで、この問題に対処する解決策があります。

+
func concat(values []string) string {
+    sb := strings.Builder{}
+    for _, value := range values {
+        _, _ = sb.WriteString(value)
+    }
+    return sb.String()
+}
+
+

各反復中に、value の内容を内部バッファに追加する WriteString メソッドを呼び出して結果の文字列を構築し、メモリのコピーを最小限に抑えることができました。

+
+補足 +

WriteString は 2 番目の出力としてエラーを返しますが、意図的に無視しましょう。実際、このメソッドは nil エラー以外を返すことはありません。では、このメソッドがシグネチャの一部としてエラーを返す目的は何でしょうか。strings.Builderio.StringWriter インタフェースを実装しており、これには WriteString(s string) (n int, err error) という1つのメソッドが含まれています。したがって、このインタフェースに準拠するには、WriteString はエラーを返さなければならないのです。

+
+

内部的には、strings.Builder はバイトスライスを保持します。 WriteString を呼び出すたびに、このスライスに追加する呼び出しが行われます。これには2つの影響があります。まず、 append の呼び出しが衝突状態を引き起こす可能性があるため、この構造体は同時に使用されるべきではありません。2番目の影響は、 非効率なスライスの初期化 (#21) で見たものです。スライスの将来の長さがすでにわかっている場合は、それを事前に割り当てる必要があります。そのために、strings.Builder は別の n バイトのためのスペースを保証するメソッド Grow(n int) を持っています。

+
func concat(values []string) string {
+    total := 0
+    for i := 0; i < len(values); i++ {
+        total += len(values[i])
+    }
+
+    sb := strings.Builder{}
+    sb.Grow(total) (2)
+    for _, value := range values {
+        _, _ = sb.WriteString(value)
+    }
+    return sb.String()
+}
+
+

ベンチマークを実行して 3 つのバージョン( += を使用した V1 、事前割り当てなしで strings.Builder{} を使用した V2 、事前割り当てありの strings.Builder{} を使用した V3 )を比較してみましょう。入力スライスには 1,000 個の文字列が含まれており、各文字列には 1,000 バイトが含まれています。

+
BenchmarkConcatV1-4             16      72291485 ns/op
+BenchmarkConcatV2-4           1188        878962 ns/op
+BenchmarkConcatV3-4           5922        190340 ns/op
+
+

ご覧のとおり、最新バージョンが最も効率的で、V1 より 99% 、V2 より 78% 高速です。

+

strings.Builder は、文字列のリストを連結するための解決策として推奨されます。通常、これはループ内で使用する必要があります。いくつかの文字列 (名前と姓など)を連結するだけの場合、 strings.Builder の使用は、 += 演算子や fmt.Sprintf と比べて可読性が低くなるからです。

+

ソースコード

+

無駄な文字列変換 (#40)

+
+要約 +

bytes パッケージは strings パッケージと同じ操作を提供してくれることを覚えておくと、余分なバイト・文字列変換を避けることができます。

+
+

文字列または []byte を扱うことを選択する場合、ほとんどのプログラマーは利便性のために文字列を好む傾向があります。しかし、ほとんどの I/O は実際には []byte で行われます。たとえば、io.Readerio.Writer、および io.ReadAll は文字列ではなく []byte を処理します。

+

文字列と []byte のどちらを扱うべきか迷ったとき、[]byte を扱う方が必ずしも面倒だというわけではないことを思い出してください。strings パッケージからエクスポートされたすべての関数には、bytes パッケージに代替機能があります。 SplitCountContainsIndex などです。したがって、I/O を実行しているかどうかに関係なく、文字列の代わりにバイトを使用してワークフロー全体を実装でき、追加の変換コストを回避できるかどうかを最初に確認しましょう。

+

ソースコード

+

部分文字列とメモリリーク (#41)

+
+要約 +

部分文字列の代わりにコピーを使用すると、部分文字列操作によって返される文字列が同じバイト配列によってサポートされるため、メモリリークを防ぐことができます。

+
+

スライスとメモリリーク (#26) では、スライスまたは配列のスライスがメモリリークの状況を引き起こす可能性があることを確認しました。この原則は、文字列および部分文字列の操作にも当てはまります。

+

Go言語で部分文字列操作を使用するときは、2 つのことに留意する必要があります。まず、提供される間隔はルーン数ではなく、バイト数に基づいています。次に、結果の部分文字列が最初の文字列と同じバッキング配列を共有するため、部分文字列操作によりメモリリークが発生する可能性があります。これを防ぐ方法は、文字列のコピーを手動で実行するか、Go 1.18 から実装されている strings.Clone を使用することです。

+

ソースコード

+

関数とメソッド

+

どの型のレシーバーを使用すればよいかわかっていない (#42)

+
+要約 +

値レシーバーとポインタレシーバーのどちらを使用するかは、どの型なのか、変化させる必要があるかどうか、コピーできないフィールドが含まれているかどうか、オブジェクトはどれくらい大きいのか、などの要素に基づいて決定する必要があります。分からない場合は、ポインタレシーバを使用してください。

+
+

値レシーバーとポインタレシーバーのどちらを選択するかは、必ずしも簡単ではありません。選択に役立ついくつかの条件について説明しましょう。

+

ポインタレシーバーでなければならない とき

+
    +
  • メソッドがレシーバーを変化させる必要がある場合。このルールは、受信側がスライスであり、メソッドが要素を追加する必要がある場合にも有効です。
  • +
+
type slice []int
+
+func (s *slice) add(element int) {
+    *s = append(*s, element)
+}
+
+
    +
  • メソッドレシーバーにコピーできないフィールドが含まれている場合。sync パッケージの型部分はその一例になります( sync 型のコピー (#74) を参照)。
  • +
+

ポインタレシーバーであるべき とき

+
    +
  • レシーバーが大きなオブジェクトの場合。ポインタを使用すると、大規模なコピーの作成が防止されるため、呼び出しがより効率的になります。どれくらいの大きさなのか確証がない場合は、ベンチマークが解決策になる可能性があります。多くの要因に依存するため、特定のサイズを指定することはほとんど不可能です。
  • +
+

値レシーバーでなければならない とき

+
    +
  • レシーバーの不変性を強制する必要がある場合。
  • +
  • レシーバーがマップ、関数、チャネルの場合。それ以外の場合はコンパイルエラーが発生します。
  • +
+

値レシーバーであるべき とき

+
    +
  • レシーバーが変化させる必要のないスライスの場合。
  • +
  • レシーバーが、time.Time などの小さな配列または構造体で、可変フィールドを持たない値型である場合。
  • +
  • レシーバーが intfloat64、または string などの基本型の場合。
  • +
+

もちろん、特殊なケースは常に存在するため、すべてを網羅することは不可能ですが、このセクションの目標は、ほとんどのケースをカバーするためのガイダンスを提供することです。通常は、そうしない正当な理由がない限り、値レシーバーを使用して間違いありません。分からない場合は、ポインタレシーバを使用する必要があります。

+

ソースコード

+

名前付き結果パラメータをまったく使用していない (#43)

+
+要約 +

名前付き結果パラメーターの使用は、特に複数の結果パラメーターが同じ型を持つ場合、関数・メソッドの読みやすさを向上させる効率的な方法です。場合によっては、名前付き結果パラメータはゼロ値に初期化されるため、この方法が便利ですらあることもあります。ただし潜在的な副作用には注意してください。

+
+

関数またはメソッドでパラメータを返すとき、これらのパラメータに名前を付けて、通常の変数として使用できます。結果パラメーターに名前を付けると、関数・メソッドの開始時にそのパラメーターはゼロ値に初期化されます。名前付き結果パラメータを使用すると、 むき出しの return 文(引数なし) を呼び出すこともできます。その場合、結果パラメータの現在の値が戻り値として使用されます。

+

以下は、名前付き結果パラメータ b を用いた例です。

+
func f(a int) (b int) {
+    b = a
+    return
+}
+
+

この例では、結果パラメータに名前 b を付けています。引数なしで return を呼び出すと、b の現在の値が返されます。

+

場合によっては、名前付きの結果パラメーターによって可読性が向上することもあります。たとえば、2 つのパラメーターが同じ型である場合などです。その他にも、利便性のために用いることができます。ゆえに、明確な利点がある場合は、慎重になりながらも名前付き結果パラメータを使用するべきです。

+

ソースコード

+

名前付き結果パラメータによる予想外の副作用 (#44)

+
+要約 +

#43 を参照してください。

+
+

名前付き結果パラメータが状況によっては役立つ理由について説明しました。 ただし、これらはゼロ値に初期化されるため、十分に注意しないと、軽微なバグが発生する可能性があります。たとえば、このコードはどこが間違っているでしょうか。

+
func (l loc) getCoordinates(ctx context.Context, address string) (
+    lat, lng float32, err error) {
+    isValid := l.validateAddress(address) (1)
+    if !isValid {
+        return 0, 0, errors.New("invalid address")
+    }
+
+    if ctx.Err() != nil { (2)
+        return 0, 0, err
+    }
+
+    // 座標を取得して返す
+}
+
+

一瞥しただけではエラーは明らかではないかもしれません。if ctx.Err() != nil スコープで返されるエラーは err です。しかし、err 変数には値を割り当てていません。error 型のゼロ値、 nil に割り当てられたままです。したがって、このコードは常に nil エラーを返します。

+

名前付き結果パラメータを使用する場合、各パラメータはゼロ値に初期化されることに注意してください。このセクションで説明したように、これにより、見つけるのが必ずしも簡単ではない軽微なバグが発生する可能性があります。ゆえに、潜在的な副作用を避けるために、名前付き結果パラメーターを使用するときは注意してください。

+

ソースコード

+

nil レシーバーを返す (#45)

+
+要約 +

インタフェースを返すときは、nil ポインタを返すのではなく、明示的な nil 値を返すように注意してください。そうしなければ、意図しない結果が発生し、呼び出し元が nil ではない値を受け取る可能性があります。

+
+ + +

ソースコード

+

関数入力にファイル名を使用している (#46)

+
+要約 +

ファイル名の代わりに io.Reader 型を受け取るように関数を設計すると、関数の再利用性が向上し、テストが容易になります。

+
+

ファイル名をファイルから読み取るための関数入力として受け入れることは、ほとんどの場合、「コードの臭い」とみなされるべきです( os.Open などの特定の関数を除く)。複数のファイルを作成することにになるかもしれず、単体テストがより複雑になる可能性があるからです。また、関数の再利用性も低下します (ただし、すべての関数が再利用されるわけではありません)。 io.Reader インタフェースを使用すると、データソースが抽象化されます。入力がファイル、文字列、HTTP リクエスト、gRPC リクエストのいずれであるかに関係なく、実装は再利用でき、簡単にテストできます。

+

ソースコード

+

defer 引数とレシーバーがどのように評価されるかを知らない(引数の評価、ポインター、および値レシーバー) (#47)

+
+要約 +

ポインタを defer 関数に渡すことと、呼び出しをクロージャ内にラップすることが、引数とレシーバーの即時評価を克服するために実現可能な解決策です。

+
+

defer 関数では、引数は、上位ブロックの関数が戻ってからではなく、すぐに評価されます。たとえば、このコードでは、常に同じステータス――空の文字列――で notifyincrementCounter を呼び出します。

+
const (
+    StatusSuccess  = "success"
+    StatusErrorFoo = "error_foo"
+    StatusErrorBar = "error_bar"
+)
+
+func f() error {
+    var status string
+    defer notify(status)
+    defer incrementCounter(status)
+
+    if err := foo(); err != nil {
+        status = StatusErrorFoo
+        return err
+    }
+
+    if err := bar(); err != nil {
+        status = StatusErrorBar
+        return err
+    }
+
+    status = StatusSuccess
+    return nil
+}
+
+

たしかに、notify(status)incrementCounter(status)defer 関数として呼び出しています。したがって、Go言語は、defer を使用した段階で f がステータスの現在の値を返すと、これらの呼び出しの実行を遅らせ、空の文字列を渡します。

+

defer を使い続けたい場合の主な方法は 2 つあります。

+

最初の解決策は文字列ポインタを渡すことです。

+
func f() error {
+    var status string
+    defer notify(&status) 
+    defer incrementCounter(&status)
+
+    // 関数のそれ以外の部分は変更なし
+}
+
+

defer を使用すると、引数(ここではステータスのアドレス)がすぐに評価されます。ステータス自体は関数全体で変更されますが、そのアドレスは割り当てに関係なく一定のままです。よって、notify または incrementCounter が文字列ポインタによって参照される値を使用する場合、期待どおりに動作します。ただし、この解決策では 2 つの関数のシグネチャを変更する必要があり、それが常に可能であるとは限りません。

+

別の解決策があります――クロージャ(本体の外部から変数を参照する匿名関数値)を defer 文として呼び出すことです。

+
func f() error {
+    var status string
+    defer func() {
+        notify(status)
+        incrementCounter(status)
+    }()
+
+    // 関数のそれ以外の部分は変更なし
+}
+
+

ここでは、notifyincrementCounter の両方の呼び出しをクロージャ内にラップします。このクロージャは、本体の外部からステータス変数を参照します。ゆえに、status は、defer を呼び出したときではなく、クロージャが実行されたときに評価されます。この解決策は正しく機能する上に、シグネチャを変更するために notifyincrementCounter を必要としません。

+

この動作はメソッドレシーバーにも適用されることにも注意してください。レシーバーはすぐに評価されます。

+

ソースコード

+

エラー処理

+

パニック (#48)

+
+要約 +

panic の使用は、Go言語でエラーに対処するための手段です。ただし、これは回復不能な状況でのみ使用するようにしてください。たとえば、ヒューマンエラーを通知する場合や、必須の依存関係の読み込みに失敗した場合などです。

+
+

Go言語では、panic は通常の流れを停止する組み込み関数です。

+
func main() {
+    fmt.Println("a")
+    panic("foo")
+    fmt.Println("b")
+}
+
+

このコードは a を出力し、b を出力する前に停止します。

+
a
+panic: foo
+
+goroutine 1 [running]:
+main.main()
+        main.go:7 +0xb3
+
+

panic の使用は慎重にすべきです。代表的なケースが 2 つあり、1 つはヒューマンエラーを通知する場合(例: sql.Registerドライバーが nil または既に登録されている場合に panic を起こします)、もう 1 つはアプリケーションが必須の依存関係の生成に失敗した場合です。結果として、例外的にアプリケーションを停止します。それ以外のほとんどの場合においては、エラー処理は、最後の戻り引数として適切なエラー型を返す関数を通じて行うべきです。

+

ソースコード

+

エラーをラップすべきときを知らない (#49)

+
+要約 +

エラーをラップすると、エラーをマークしたり、追加のコンテキストを提供したりできます。ただし、エラーラッピングにより、呼び出し元がソースエラーを利用できるようになるため、潜在的な結合が発生します。それを避けたい場合は、エラーラッピングを使用しないでください。

+
+

Go 1.13 以降、%w ディレクティブを使用すれば簡単にエラーをラップできるようになりました。エラーラッピングとは、ソースエラーも使用できるようにするラッパーコンテナ内でエラーをラップまたはパックすることです。一般に、エラーラッピングの主な使用例は次の 2 つです。

+
    +
  • エラーにさらにコンテキストを加える
  • +
  • エラーを特定のエラーとしてマークする
  • +
+

エラーを処理するとき、エラーをラップするかどうかを決定できます。ラッピングとは、エラーにさらにコンテキストを追加したり、エラーを特定のタイプとしてマークしたりすることです。エラーをマークする必要がある場合は、独自のエラー型を作成する必要があります。ですが、新たにコンテキストを加えたいだけの場合は、新しいエラー型を作成する必要がないため、%w ディレクティブを指定して fmt.Errorf を使用しましょう。ただし、エラーラッピングにより、呼び出し元がソースエラーを利用できるようになるため、潜在的な結合が生じます。それを避けたい場合は、エラーのラッピングではなく、エラーの変換を使用する必要があります。たとえば、%v ディレクティブを指定した fmt.Errorf を使用します。

+

ソースコード

+

エラー型の不正な比較 (#50)

+
+要約 +

Go 1.13 のエラーラッピングを %w ディレクティブと fmt.Errorf で使用する場合、型に対するエラーの比較は errors.As を通じて行う必要があります。そうでなければ、返されたエラーがラップされている場合、評価に失敗します。

+
+ + +

ソースコード

+

エラー値の不正な比較 (#51)

+
+要約 +

Go 1.13 のエラーラッピングを %w ディレクティブと fmt.Errorf で使用する場合、エラーと値の比較は errors.As を通じて行う必要があります。そうでなければ、返されたエラーがラップされている場合、評価に失敗します。

+
+

センチネルエラーはグローバル変数として定義されたエラーのことです。

+

import "errors"
+
+var ErrFoo = errors.New("foo")
+
+一般に、慣例として Err で始め、その後にエラー型を続けます。ここでは ErrFoo です。センチネルエラーは、予期される エラー、つまりクライアントが確認することを期待するエラーを伝えます。一般的なガイドラインとして

+
    +
  • 予期されるエラーはエラー値(センチネルエラー)として設計する必要があります: var ErrFoo =errors.New("foo")
  • +
  • 予期しないエラーはエラー型として設計する必要があります: BarErrorerror インタフェースを実装した上で type BarError struct { ... }
  • +
+

アプリケーションで %w ディレクティブと fmt.Errorf を使用してエラーラップを使用する場合、特定の値に対するエラーのチェックは == の代わりに errors.Is を使用して行いましょう。それによって、センチネルエラーがラップされている場合でも、errors.Is はそれを再帰的にアンラップし、チェーン内の各エラーを提供された値と比較できます。

+

ソースコード

+

エラーの 2 回処理 (#52)

+
+要約 +

ほとんどの場合、エラーは 1 回で処理されるべきです。エラーをログに記録することは、エラーを処理することです。すなわち、ログに記録するかエラーを返すかを選択する必要があります。多くの場合、エラーラッピングは、エラーに追加のコンテキストを提供し、ソースエラーを返すことができるため、解決策になります。

+
+

エラーを複数回処理することは、特にGo言語に限らず、開発者が頻繁にやってしまうミスです。これにより、同じエラーが複数回ログに記録され、デバッグが困難になる状況が発生する可能性があります。

+

エラー処理は 1 度で済ますべきだということを覚えておきましょう。エラーをログに記録することは、エラーを処理することです。つまり、行うべきは、ログに記録するか、エラーを返すかのどちらかだということです。これにより、コードが簡素化され、エラーの状況についてより適切な洞察が得られます。エラーラッピングは、ソースエラーを伝え、エラーにコンテキストを追加できるため、最も使い勝手の良い手段になります。

+

ソースコード

+

エラー処理をしない (#53)

+
+要約 +

関数呼び出し中であっても、defer 関数内であっても、エラーを無視するときは、ブランク識別子を使用して明確に行うべきです。そうしないと、将来の読み手がそれが意図的だったのか、それともミスだったのか困惑する可能性があります。

+
+

ソースコード

+

defer エラーを処理しない (#54)

+
+要約 +

多くの場合、defer 関数によって返されるエラーを無視すべきではありません。状況に応じて、直接処理するか、呼び出し元に伝えましょう。これを無視する場合は、ブランク識別子を使用してください。

+
+

次のコードを考えてみましょう。

+
func f() {
+  // ...
+  notify() // エラー処理は省略されています
+}
+
+func notify() error {
+  // ...
+}
+
+

保守性の観点から、このコードはいくつかの問題を引き起こす可能性があります。ある人がこれを読むことを考えてみます。読み手は、notify がエラーを返すにもかかわらず、そのエラーが親関数によって処理されないことに気づきます。エラー処理が意図的であるかどうかを果たして推測できるでしょうか。以前の開発者がそれを処理するのを忘れたのか、それとも意図的に処理したのかを知ることができるでしょうか。

+

これらの理由により、エラーを無視したい場合、ブランク識別子( _ )を使うほかありません。

+
_ = notify
+
+

コンパイルと実行時間の点では、この方法は最初のコード部分と比べて何も変わりません。しかし、この新しいバージョンでは、私たちがエラーに関心がないことを明らかにしています。また、エラーが無視される理由を示すコメントを追加することもできます。

+
// 最大でも 1 回の伝達 
+// それゆえ、エラーが発生した場合にそれらの一部が失われることは許容されます
+_ = notify()
+
+

ソースコード

+

並行処理:基礎

+

並行処理と並列処理の混同 (#55)

+
+要約 +

並行処理と並列処理の基本的な違いを理解することは、 Go 開発者にとって必須です。並行処理は構造に関するものですが、並列処理は実行に関するものです。

+
+

並行処理と並列処理は同じではありません。

+
    +
  • 並行処理は構造に関するものです。別々の並行ゴルーチンが取り組むことができるさまざまな段階を導入することで、逐次処理を並行処理に変更できます。
  • +
  • 並列処理は実行に関するものです。並列ゴルーチンをさらに追加することで、段階レベルで並列処理を使用できます。
  • +
+

まとめると、並行処理は、並列化できる部分をもつ問題を解決するための構造を提供します。すなわち、並行処理により並列処理が可能 になります 。

+ + +

並行処理のほうが常に早いと考えている (#56)

+
+要約 +

熟練した開発者になるには、並行処理が必ずしも高速であるとは限らないことを認識する必要があります。最小限のワークロードの並列処理を伴う解決策は、必ずしも逐次処理より高速であるとは限りません。逐次処理と並行処理のベンチマークは、仮定を検証する方法であるべきです。

+
+

セクション全文はこちら

+

ソースコード

+

チャネルまたはミューテックスをいつ使用するべきかについて戸惑っている (#57)

+
+要約 +

ゴルーチンの相互作用を認識していることは、チャネルとミューテックスのどちらを選択するかを決定するときにも役立ちます。一般に、並列ゴルーチンには同期が必要であり、したがってミューテックスが必要です。反対に、並行ゴルーチンは通常、調整とオーケストレーション、つまりチャネルを必要とします。

+
+

並行処理の問題を考慮すると、チャネルまたはミューテックスを使用した解決策を実装できるかどうかが必ずしも明確ではない可能性があります。Go言語は通信によるメモリの共有を促進するため、起こりうる間違いのうちの一つは、ユースケースにかかわらず、チャネルの使用を常に強制することです。しかしながら、2 つの方法は補完的なものであると見なすべきです。

+

チャネルまたはミューテックスはどのような場合に使用する必要があるのでしょうか。次の図の例をバックボーンとして使用します。この例には、特定の関係を持つ 3 つの異なるゴルーチンがあります。

+
    +
  • G1 と G2 は並列ゴルーチンです。チャネルからメッセージを受信し続ける同じ関数を実行する 2 つのゴルーチン、あるいは同じ HTTP ハンドラを同時に実行する 2 つのゴルーチンかもしれません。
  • +
  • G1 と G3 は並行ゴルーチンであり、G2 と G3 も同様です。すべてのゴルーチンは全体の並行構造の一部ですが、G1 と G2 が最初のステップを実行し、G3 が次のステップを実行します。
  • +
+ + +

原則として、並列ゴルーチンは、スライスなどの共有リソースにアクセスしたり変更したりする必要がある場合などに、_同期_する必要があります。同期はミューテックスでは強制されますが、どのチャネル型でも強制されません(バッファありチャネルを除く)。したがって、一般に、並列ゴルーチン間の同期はミューテックスを介して達成される必要があります。

+

一方、一般に、並行ゴルーチンは 調整およびオーケストレーション をする必要があります。たとえば、G3 が G1 と G2 の両方からの結果を集約する必要がある場合、G1 と G2 は新しい中間結果が利用可能であることを G3 に通知する必要があります。この調整はコミュニケーションの範囲、つまりチャネルに該当します。

+

並行ゴルーチンに関しては、リソースの所有権をあるステップ(G1 および G2)から別のステップ(G3)に移管したい場合もあります。たとえば、G1 と G2 によって共有リソースが豊かになっている場合、ある時点でこのジョブは完了したと見なされます。ここでは、チャネルを使用して、特定のリソースの準備ができていることを通知し、所有権の移転を処理する必要があります。

+

ミューテックスとチャネルには異なるセマンティクスがあります。ステートを共有したいとき、または共有リソースにアクセスしたいときは、ミューテックスによってこのリソースへの排他的アクセスが保証されます。反対に、チャネルはデータの有無(chan struct{} の有無)に関係なくシグナルを行う仕組みです。調整や所有権の移転はチャネルを通じて行う必要があります。ゴルーチンが並列か並行かを知ることが重要です。一般に、並列ゴルーチンにはミューテックスが必要で、並行ゴルーチンにはチャネルが必要です。

+

競合問題を理解していない(データ競合と競合状態、そしてGo言語のメモリモデル) (#58)

+
+要約 +

並行処理に熟達するということは、データ競合と競合状態が異なる概念であることを理解することも意味します。データ競合は、複数のゴルーチンが同じメモリ位置に同時にアクセスし、そのうちの少なくとも 1 つが書き込みを行っている場合に発生します。一方、データ競合がないことが必ずしも決定的実行を意味するわけではありません。動作が制御できないイベントの順序やタイミングに依存している場合、これは競合状態です。

+
+

競合問題は、プログラマーが直面する可能性のあるバグの中で最も困難かつ最も潜伏性の高いバグの 1 つとなります。Go 開発者として、私たちはデータ競合と競合状態、それらが及ぼしうる影響、およびそれらを回避する方法などの重要な側面を理解する必要があります。

+

データ競合

+

データ競合は、2 つ以上のゴルーチンが同じメモリ位置に同時にアクセスし、少なくとも 1 つが書き込みを行っている場合に発生します。この場合、危険な結果が生じる可能性があります。さらに悪いことに、状況によっては、メモリ位置に無意味なビットの組み合わせを含む値が保持されてしまう可能性があります。

+

さまざまな手法を駆使して、データ競合の発生を防ぐことができます。たとえば

+
    +
  • sync/atomic パッケージを使用する
  • +
  • 2 つのゴルーチンを同期する際にミューテックスのような特定の目的のためのデータ構造を利用する
  • +
  • チャネルを使用して 2 つのゴルーチンが通信し、変数が一度に 1 つのゴルーチンだけによって更新されるようにする
  • +
+

競合状態

+

実行したい操作に応じて、データ競合のないアプリケーションが必ずしも決定的な結果を意味するでしょうか。そうとはいえません。

+

競合状態は、動作が制御できないイベントのシーケンスまたはタイミングに依存する場合に発生します。ここでは、イベントのタイミングがゴルーチンの実行順序です。

+

まとめると、並行処理のアプリケーションで作業する場合、データ競合は競合状態とは異なることを理解することが不可欠です。データ競合は、複数のゴルーチンが同じメモリ位置に同時にアクセスし、そのうちの少なくとも 1 つが書き込みを行っている場合に発生します。データ競合とは、予想外の動作を意味します。ただし、データ競合のないアプリケーションが必ずしも決定的な結果を意味するわけではありません。データ競合がなくても、アプリケーションは制御されていないイベント(ゴルーチンの実行、チャネルへのメッセージの発信速度、データベースへの呼び出しの継続時間など)に依存する挙動を持つことがあります。その場合は競合状態です。並行処理のアプリケーションの設計に熟練するには、両方の概念を理解することが肝要です。

+

ソースコード

+

ワークロードタイプごとの並行処理の影響を理解していない (#59)

+
+要約 +

一定数のゴルーチンを作成するときは、ワークロードのタイプを考慮してください。CPU バウンドのゴルーチンを作成するということは、この数を GOMAXPROCS 変数(デフォルトではホスト上の CPU コアの数に基づく)に近づけることを意味します。I/O バウンドのゴルーチンの作成は、外部システムなどの他の要因に依存します。

+
+

プログラミングでは、ワークロードの実行時間は次のいずれかによって制限されます。

+
    +
  • CPU の速度 - たとえば、マージソートアルゴリズムの実行がこれにあたります。このワークロードは CPU バウンドと呼ばれます。
  • +
  • I/O の速度 - たとえば、REST 呼び出しやデータベースクエリの実行がこれにあたります。このワークロードは I/O バウンドと呼ばれます。
  • +
  • 利用可能なメモリの量 - このワークロードはメモリバウンドと呼ばれます。
  • +
+
+補足 +

ここ数十年でメモリが非常に安価になったことを考慮すると、 3 つ目は現在では最もまれです。したがって、このセクションでは、最初の 2 つのワークロードタイプ、CPU バウンドと I/O バウンドに焦点を当てます。

+
+

ワーカーによって実行されるワークロードが I/O バウンドである場合、値は主に外部システムに依存します。逆に、ワークロードが CPU に依存している場合、ゴルーチンの最適な数は利用可能な CPU コアの数に近くなります(ベストプラクティスは runtime.GOMAXPROCS を使用することです)。並行処理のアプリケーションを設計する場合、ワークロードのタイプ( I/O あるいは CPU )を知ることが重要です。

+

ソースコード

+

Go Context に対する誤解 (#60)

+
+要約 +

Go Context は、Go言語の並行処理の基礎の一部でもあります。 Context を使用すると、デッドライン、キーと値のリストを保持できます。

+
+
+

https://pkg.go.dev/context

+

Context は、デッドライン、キャンセルシグナル、その他の値を API の境界を越えて伝達します。

+
+

デッドライン

+

デッドラインとは、次のいずれかで決定される特定の時点を指します。

+
    +
  • 現在からの time.Duration (例:250 ms)
  • +
  • time.Time (例:2023-02-07 00:00:00 UTC)
  • +
+

デッドラインのセマンティクスは、これを過ぎた場合は進行中のアクティビティを停止する必要があることを伝えます。アクティビティとは、たとえば、チャネルからのメッセージの受信を待機している I/O リクエストやゴルーチンです。

+

キャンセルシグナル

+

Go Context のもう 1 つの使用例は、キャンセルシグナルを伝送することです。別のゴルーチン内で CreateFileWatcher(ctx context.Context, filename string) を呼び出すアプリケーションを作成することを想像してみましょう。この関数は、ファイルから読み取りを続けて更新をキャッチする特定のファイルウォッチャーを作成します。提供された Context が期限切れになるかキャンセルされると、この関数はそれを処理してファイル記述子を閉じます。

+

Context Value

+

Go Context の最後の使用例は、キーと値のリストを運ぶことです。 Context にキーと値のリストを含める意味は何でしょうか。Go Context は汎用的であるため、使用例は無限にあります。

+

たとえば、トレースを使用する場合、異なるサブ関数の間で同じ相関 ID を共有したいことがあるかもしれません。一部の開発者は、この ID を関数シグネチャの一部にするにはあまりに侵略的だと考えるかもしれません。この点に関して、与えられた Context の一部としてそれを含めることを決定することもできます。

+

Context のキャンセルをキャッチする

+

context.Context タイプは、受信専用の通知チャネル <-chan struct{} を返す Done メソッドをエクスポートします。このチャネルは、 Context に関連付けられた作業をキャンセルする必要がある場合に閉じられます。たとえば

+
    +
  • context.WithCancelで作成された Context に関連する Done チャネルは、cancel関数が呼び出されると閉じられます。
  • +
  • context.WithDeadlineで作成した Context に関連する Done チャネルは、デッドラインを過ぎると閉じられます。
  • +
+

注意すべき点の 1 つは、内部チャネルは、特定の値を受け取ったときではなく、 Context がキャンセルされたとき、またはデッドラインに達したときに閉じる必要があるということです。チャネルのクローズは、すべての消費者ゴルーチンが受け取る唯一のチャネルアクションであるためです。このようにして、 Context がキャンセルされるか、デッドラインに達すると、すべての消費者に通知が届きます。

+

まとめると、熟練した Go 開発者になるには、 Context とその使用方法について理解する必要があります。原則として、ユーザーが待機させられる関数は Context を取得するべきです。これにより、上流の呼び出し元がこの関数をいつ呼び出すかを決定できるようになるからです。

+

ソースコード

+

並行処理:実践

+

不適切な Context を広めてしまう (#61)

+
+要約 +

Context を伝播する際には、Context をキャンセルできる条件を理解することが重要です。たとえば、レスポンスが送信された際に HTTP ハンドラが Context をキャンセルするときなどです。

+
+

多くの状況では、Go Context を伝播することが推奨されます。ただし、Context の伝播によって軽微なバグが発生し、サブ関数が正しく実行されなくなる場合があります。

+

次の例を考えてみましょう。いくつかのタスクを実行してレスポンスを返す HTTP ハンドラを公開します。ただし、レスポンスを返す直前に、それを Kafka トピックに送信したいと思っています。HTTP コンシューマにレイテンシの点でペナルティを課したくないので、publish アクションを新しいゴルーチン内で非同期に処理したいと考えています。たとえば、Context がキャンセルされた場合にメッセージの publish アクションを中断できるように、Context を受け入れる publish 関数を自由に使えるとします。可能な実装は次のとおりです。

+
func handler(w http.ResponseWriter, r *http.Request) {
+    response, err := doSomeTask(r.Context(), r)
+    if err != nil {
+        http.Error(w, err.Error(), http.StatusInternalServerError)
+    return
+    }
+    go func() {
+        err := publish(r.Context(), response)
+        // err の処理をする
+    }()
+    writeResponse(response)
+}
+
+

このコードの何が問題なのでしょうか。HTTP リクエストに付された Context は、さまざまな状況でキャンセルされる可能性があることを知っておく必要があります。

+
    +
  • クライアントの接続が終了したとき
  • +
  • HTTP/2リクエストの場合、リクエストがキャンセルされたとき
  • +
  • クライアントにレスポンスが書き戻されたとき
  • +
+

最初の 2 つのケースでは、処理はおそらく正しく行われます。たとえば、doSomeTask からレスポンスを受け取ったものの、クライアントが接続を閉じた場合、メッセージが publish されないように、Context が既にキャンセルされた状態で publish を呼び出しても問題はおそらく起きません。しかし、最後のケースはどうでしょうか。

+

レスポンスがクライアントに書き込まれると、要求に関連付けられた Context がキャンセルされます。したがって、競合状態に直面します。

+
    +
  • レスポンスが Kafka の publish 後に書かれた場合、レスポンスを返し、メッセージを正常に公開します。
  • +
  • ただし、Kafka の publish 前または publish 中にレスポンスが書かれた場合、メッセージは publish されるべきではありません。
  • +
+

後者の場合、HTTP レスポンスをすぐに返すので、publish を呼び出すとエラーが返されます。

+
+補足 +

Go 1.21 からは、キャンセルせずに新しい Context を作成する方法が追加されました。 context.WithoutCancel は、親がキャンセルされたときにキャンセルされていない親のコピーを返します。

+
+

まとめると、Context の伝播は慎重に行う必要があります。

+

ソースコード

+

停止すべきときを知らずにゴルーチンを開始してしまう (#62)

+
+要約 +

リークを避けることは、ゴルーチンが開始されるたびに、最終的に停止する必要があることを意味します。

+
+

ゴルーチンは簡単に行うことができます。非常に簡単であるため、新しいゴルーチンをいつ停止するかについての計画を必ずしも立てていない可能性があり、リークにつながることがあります。ゴルーチンをいつ停止すればよいかわからないのは、Go言語でよくある設計上の問題であり、並行処理におけるミスです。

+

具体的な例について説明しましょう。外部設定(データベース接続などを使用したものなど)を監視する必要があるアプリケーションを設計します。まず、次のような実装をしてみます。

+
func main() {
+    newWatcher()
+    // アプリケーションを実行する
+}
+
+type watcher struct { /* いくつかのリソース */ }
+
+func newWatcher() {
+    w := watcher{}
+    go w.watch() // 外部設定を監視するゴルーチンを作成する
+}
+
+

このコードの問題は、メインのゴルーチンが終了すると(おそらく OS シグナルまたは有限のワークロードのため)、アプリケーションが停止してしまうことです。したがって、ウォッチャーによって作成されたリソースは正常に閉じられません。これを防ぐにはどうすればよいでしょうか。

+

1 つの方法としては、main が戻ったときにキャンセルされる Context を newWatcher に渡すことが挙げられます。

+
func main() {
+    ctx, cancel := context.WithCancel(context.Background())
+    defer cancel()
+    newWatcher(ctx)
+    // アプリケーションを実行する
+}
+
+func newWatcher(ctx context.Context) {
+    w := watcher{}
+    go w.watch(ctx)
+}
+
+

作成した Context を watch メソッドに伝播します。Context がキャンセルされると、ウォッチャー構造体はそのリソースを閉じます。しかし、watch がそれを行う時間が確実にあるとはいえません。これは設計上の欠陥です。

+

問題は、ゴルーチンを停止する必要があることを伝えるためにシグナルを使用したことです。リソースが閉じられるまで、親のゴルーチンをブロックしませんでした。そうならないようにしましょう。

+
func main() {
+    w := newWatcher()
+    defer w.close()
+    // アプリケーションを実行する
+}
+
+func newWatcher() watcher {
+    w := watcher{}
+    go w.watch()
+    return w
+}
+
+func (w watcher) close() {
+    // リソースを閉じる
+}
+
+

リソースを閉じる時間になったことを watcher に通知する代わりに、 defer を使用してこの close メソッドを呼び出し、アプリケーションが終了する前にリソースが確実に閉じられるようにします。

+

まとめると、ゴルーチンは他のリソースと同様、メモリや他のリソースを解放するために最終的に閉じる必要があることに注意してください。ゴルーチンをいつ停止するかを知らずに開始するのは設計上の問題です。ゴルーチンが開始されるときは常に、いつ停止するかについて明確な計画を立てる必要があります。最後になりましたが、ゴルーチンがリソースを作成し、その有効期間がアプリケーションの存続期間にバインドされている場合は、アプリケーションを終了する前にそのゴルーチンが完了するのを待った方がおそらく確実です。そうすることで、リソースを間違いなく解放できます。

+

ソースコード

+

ゴルーチンとループ変数に注意しない (#63)

+
+注意 +

このミスは Go 1.22 からは気にする必要がありません(詳細)。

+
+

select とチャネルを使用して決定的動作を期待する (#64)

+
+要約 +

複数のオプションが可能な場合、複数のチャネルで select するとケースがランダムに選択されることを理解すると、並行処理における軽微なバグにつながる可能性のある誤った仮定を立てることがなくなります。

+
+

Go 開発者がチャネルを操作するときにありがちな間違いの 1 つは、select が複数のチャネルでどのように動作するかについて誤った理解をすることです。

+

たとえば、次の場合を考えてみましょう( disconnectCh はバッファなしチャネルです)。

+
go func() {
+  for i := 0; i < 10; i++ {
+      messageCh <- i
+    }
+    disconnectCh <- struct{}{}
+}()
+
+for {
+    select {
+    case v := <-messageCh:
+        fmt.Println(v)
+    case <-disconnectCh:
+        fmt.Println("disconnection, return")
+        return
+    }
+}
+
+

この例を複数回実行した場合、結果はランダムになります。

+
0
+1
+2
+disconnection, return
+
+0
+disconnection, return
+
+

どういうわけか 10 通のメッセージを消費するのではなく、そのうちの数通だけを受信しました。これは、複数のチャネルと併用した場合の select 文の仕様によるものです(https:// go.dev/ref/spec)。

+
+

Quote

+

1 つ以上の通信を続行できる場合、均一の擬似ランダム選択によって、続行できる 1 つの通信が選択されます。

+
+

最初に一致したケースが優先される switch 文とは異なり、select 文は複数のオプションが可能な場合にランダムに選択します。

+

この動作は最初は奇妙に思えるかもしれません。しかし、これはスタベーションを防ぐという理由があってのことです。最初に選択された通信がソースの順序に基づいているとします。その場合、送信速度が速いために、たとえば 1 つのチャネルからしか受信できないという状況に陥る可能性があります。これを防ぐために、Go言語の設計者はランダム選択を使用することにしました。

+

複数のチャネルで select を使用する場合、複数のオプションがあるなら、ソース順序の最初のケースが自動的に優先されるわけではないことに注意する必要があります。代わりに、Go言語はランダムに選択するため、どのオプションが選択されるかは保証されません。この動作を克服するには、単一の生産者ゴルーチンの場合、バッファなしのチャネルまたは単一のチャネルを使用することができます。

+

ソースコード

+

通知チャネルを使用していない (#65)

+
+要約 +

chan struct{} 型を使用して通知を送信しましょう。

+
+

チャネルは、シグナルを介してゴルーチン間で通信するためのメカニズムです。シグナルにはデータが含まれているかどうかは関係ありません。

+

具体的な例を見てみましょう。通信の切断が発生するたびにそれを通知するチャネルを作成します。 1 つの方法として、これを chan bool として扱うことが挙げられます。

+
disconnectCh := make(chan bool)
+
+

ここで、そのようなチャネルを提供する API と対話するとします。これは真偽値のチャネルであるため、true または false のメッセージを受信できます。true が何を伝えているかはおそらく明らかでしょう。しかし、 false とは何を意味するのでしょうか。通信が切断されていないということでしょうか。その場合、どれくらいの頻度でそのようなシグナルを受信するのでしょうか。あるいは再接続したということでしょうか。そもそも false を受け取ることを期待すべきなのでしょうか。おそらく true メッセージを受け取ることだけを期待すべきでしょう。

+

その場合、情報を伝えるために特定の値は必要ないことを意味し、データの ない チャネルが必要になります。これを処理する慣用的な方法は、空の構造体のチャネル―― chan struct{}――を使用することです。

+

nil チャネルを使用していない (#66)

+
+要約 +

nil チャネルを使用することによって、たとえば select 文からケースを 削除 できるため、並行処理を行う際の道具の一つとして使えるようになるべきです。

+
+

次のコードによって何が行われるでしょうか。

+
var ch chan int
+<-ch
+
+

chchan int 型です。チャネルのゼロ値は nil であるので、 chnil です。ゴルーチンは panic を起こしません。ただし、永久にブロックします。

+

nil チャネルにメッセージを送信する場合も原理は同じです。以下のゴルーチンは永久にブロックします。

+
var ch chan int
+ch <- 0
+
+

では、Go言語が nil チャネルとの間でメッセージの送受信を許可する目的は何でしょうか。たとえば、2 つのチャネルをマージする慣用的な方法を実装するのに、 nil チャネルを使用することができます。

+
func merge(ch1, ch2 <-chan int) <-chan int {
+    ch := make(chan int, 1)
+
+    go func() {
+        for ch1 != nil || ch2 != nil { // 最低でも一つのチャネルが nil でなければ続行する
+            select {
+            case v, open := <-ch1:
+                if !open {
+                    ch1 = nil // 閉じたら ch1 を nil チャネルに割り当てる
+                    break
+                }
+                ch <- v
+            case v, open := <-ch2:
+                if !open {
+                    ch2 = nil // 閉じたら ch2 を nil チャネルに割り当てる
+                    break
+                }
+                ch <- v
+            }
+        }
+        close(ch)
+    }()
+
+    return ch
+}
+
+

この洗練された解決策は、nil チャネルを利用して、何らかの方法で select 文から 1 つのケースを 削除 します。

+

nil チャネルは状況によっては便利であり、Go 開発者は並行処理を扱う際に使いこなせるようになっておくべきです。

+

ソースコード

+

チャネルの容量について困惑している (#67)

+
+要約 +

問題が発生した場合は、使用するチャネルの型を慎重に決定してください。同期を強力に保証してくれるのはバッファなしチャネルのみです。

+
+

バッファありチャネル以外のチャネルの容量を指定するには正当な理由があるべきです。

+

文字列フォーマットで起こり得る副作用を忘れてしまう( etcd データ競合の例とデッドロック) (#68)

+
+要約 +

文字列の書式設定が既存の関数が呼び出す可能性があることを認識することは、デッドロックやその他のデータ競合の可能性に注意することを意味します。

+
+

ソースコード

+

append でデータ競合を起こしてしまう (#69)

+
+要約 +

append の呼び出しは必ずしもデータ競合がないわけではありません。ゆえに、共有スライス上で同時に使用してはいけません。

+
+

ソースコード

+

スライスとマップでミューテックスを正しく使用していない (#70)

+
+要約 +

スライスとマップはポインタであることを覚えておくと、典型的なデータ競合を防ぐことができます。

+
+

ソースコード

+

sync.WaitGroup を正しく使用していない (#71)

+
+要約 +

sync.WaitGroup を正しく使用するには、ゴルーチンを起動する前に Add メソッドを呼び出しましょう。

+
+

ソースコード

+

sync.Cond について忘れてしまう (#72)

+
+要約 +

sync.Cond を使用すると、複数のゴルーチンに繰り返し通知を送信できます。

+
+

ソースコード

+

errgroup を使用していない (#73)

+
+要約 +

errgroup パッケージを使用して、ゴルーチンのグループを同期し、エラーと Context を処理できます。

+
+

ソースコード

+

sync 型のコピー (#74)

+
+要約 +

sync 型はコピーされるべきではありません。

+
+

ソースコード

+

標準ライブラリ

+

間違った時間を指定する (#75)

+
+要約 +

time.Duration を受け入れる関数には注意を払ってください。整数を渡すことは許可されていますが、混乱を招かないように time API を使用するよう努めてください。

+
+

標準ライブラリの多くの関数は、int64 型のエイリアスである time.Duration を受け入れます。ただし、1 単位の time.Duration は、他のプログラミング言語で一般的に見られる 1 ミリ秒ではなく、1 ナノ秒を表します。その結果、time.Duration API を使用する代わりに数値型を渡すと、予想外の動作が発生する可能性があります。

+

他言語を使用したことのある開発者の方は、次のコードによって 1 秒周期の新しい time.Ticker が生成されると考えるかもしれません。

+
ticker := time.NewTicker(1000)
+for {
+    select {
+    case <-ticker.C:
+        // 処理をする
+    }
+}
+
+

しかしながら、1,000 time.Duration = 1,000 ナノ秒であるため、想定されている 1秒 ではなく、1,000 ナノ秒 = 1 マイクロ秒の周期になります。

+

混乱や予想外の動作を招かないよう、いつも time.Duration API を使用するべきです。

+
ticker = time.NewTicker(time.Microsecond)
+// もしくは
+ticker = time.NewTicker(1000 * time.Nanosecond)
+
+

ソースコード

+

time.After とメモリリーク (#76)

+
+要約 +

繰り返される関数(ループや HTTP ハンドラなど)で time.After の呼び出しを回避すると、ピーク時のメモリ消費を回避できます。time.After によって生成されたリソースは、 timer が終了したときにのみ解放されます。

+
+

ソースコード

+

JSON 処理でありがちな間違い (#77)

+
    +
  • 型の埋め込みによる予想外の動作
  • +
+

Go 構造体で埋め込みフィールドを使用する場合は注意してください。 なぜなら json.Marshaler インタフェースを実装する time.Time 埋め込みフィールドのようなやっかいなバグが発生して、デフォルトのマーシャリング動作がオーバーライドされる可能性があるからです。

+

ソースコード

+
    +
  • JSON と monotonic clock
  • +
+

2 つの time.Time 構造体を比較する場合、time.Time には wall clock と monotonic clock の両方が含まれており、== 演算子を使用した比較は両方の clock に対して行われることを思い出してください。

+

ソースコード

+
    +
  • any のマップ
  • +
+

JSON データのアンマーシャリング中にマップを提供するときに間違いを避けるために、数値はデフォルトで float64 に変換されることに注意してください。

+

ソースコード

+

SQL でありがちな間違い (#78)

+
    +
  • sql.Open が必ずしもデータベースへの接続を確立するわけではないことを忘れている
  • +
+

設定を試し、データベースにアクセスできることを確認する必要がある場合は、 Ping または PingContext メソッドを呼び出しましょう。

+

ソースコード

+
    +
  • コネクションプーリングのことを忘れる
  • +
+

実運用水準のアプリケーションでは、データベース接続パラメータを設定しましょう。

+
    +
  • プリペアドステートメントを使用していない
  • +
+

SQL のプリペアドステートメントを使用すると、クエリがより効率的かつ確実になります。

+

ソースコード

+
    +
  • null 値を誤った方法で処理している
  • +
+

テーブル内の null が許容されている列は、ポインタまたは sql.NullXXX 型を使用して処理しましょう。

+

ソースコード

+
    +
  • 行の反復処理によるエラーを処理しない
  • +
+

行の反復処理の後に sql.RowsErr メソッドを呼び出して、次の行の準備中にエラーを見逃していないことを確認しましょう。

+

ソースコード

+

一時的なリソース( HTTP body、sql.Rows、および os.File )を閉じていない (#79)

+
+要約 +

リークを避けるために、 io.Closer を実装しているすべての構造体を最後には閉じましょう。

+
+

ソースコード

+

HTTP リクエストに応答した後の return 文を忘れてしまう (#80)

+
+要約 +

HTTP ハンドラの実装での予想外の動作を避けるため、http.Error の後にハンドラを停止したい場合は、return 文を忘れないようにしてください。

+
+

ソースコード

+

標準の HTTP クライアントとサーバーを使用している (#81)

+
+要約 +

実運用水準のアプリケーションを求めている場合は、標準の HTTP クライアントとサーバーの実装を使用しないでください。これらの実装には、タイムアウトや稼働環境で必須であるべき動作が欠落しています。

+
+

ソースコード

+

テスト

+

テストを分類していない(ビルドタグ、環境変数、ショートモード) (#82)

+
+要約 +

ビルドフラグ、環境変数、またはショートモードを使用してテストを分類すると、テストプロセスがより効率的になります。ビルドフラグまたは環境変数を使用してテストカテゴリ(たとえば、単体テストと統合テスト)を作成し、短期間のテストと長時間のテストを区別することで、実行するテストの種類を決定できます。

+
+

ソースコード

+

-race フラグを有効にしていない (#83)

+
+要約 +

並行アプリケーションを作成する場合は、 -race フラグを有効にすることを強くお勧めします。そうすることで、ソフトウェアのバグにつながる可能性のある潜在的なデータ競合を発見できるようになります。

+
+

テスト実行モード( -parallel および -shuffle )を使用していない (#84)

+
+要約 +

-parallel フラグを使用するのは、特に長時間実行されるテストを高速化するのに効果的です。 -shuffle フラグを使用すると、テストスイートがバグを隠す可能性のある間違った仮定に依存しないようにすることができます。

+
+

テーブル駆動テストを使用しない (#85)

+
+要約 +

テーブル駆動テストは、コードの重複を防ぎ、将来の更新の処理を容易にするために、一連の類似したテストをグループ化する効率的な方法です。

+
+

ソースコード

+

単体テストでのスリープ (#86)

+
+要約 +

テストの不安定さをなくし、より堅牢にするために、同期を使用してスリープを回避しましょう。同期が不可能な場合は、リトライ手法を検討してください。

+
+

ソースコード

+

time API を効率的に処理できていない (#87)

+
+要約 +

time API を使用して関数を処理する方法を理解することで、テストの不安定さを軽減することができます。隠れた依存関係の一部として time を処理したり、クライアントに time を提供するように要求したりするなど、標準的な手段を利用できます。

+
+

ソースコード

+

テストに関するユーティリティパッケージ( httptest および iotest )を使用していない (#88)

+
    +
  • httptest パッケージは、HTTP アプリケーションを扱うのに役立ちます。クライアントとサーバーの両方をテストするための一連のユーティリティを提供します。
  • +
+

ソースコード

+
    +
  • iotest パッケージは、io.Reader を作成し、アプリケーションのエラー耐性をテストするのに役立ちます。
  • +
+

ソースコード

+

不正確なベンチマークの作成 (#89)

+
+要約 +

ベンチマークについて

+
    +
  • ベンチマークの精度を維持するには、time メソッドを使用しましょう。
  • +
  • ベンチタイムを増やすか、benchstat などのツールを使用することで、マイクロベンチマークが扱いやすくなります。
  • +
  • アプリケーションを最終的に実行するシステムがマイクロベンチマークを実行するシステムと異なる場合は、マイクロベンチマークの結果に注意してください。
  • +
  • コンパイラの最適化によってベンチマークの結果が誤魔化されないよう、テスト対象の関数が副作用を引き起こすようにしてください。
  • +
  • オブザーバー効果を防ぐには、CPU に依存する関数が使用するデータをベンチマークが再生成するよう強制してください。
  • +
+
+

セクション全文はこちら

+

ソースコード

+

Go言語のテスト機能をすべて試していない (#90)

+
    +
  • コードカバレッジ
  • +
+

コードのどの部分に注意が必要かをすぐに確認するために、-coverprofile フラグを指定してコードカバレッジを使用しましょう。

+
    +
  • 別のパッケージからのテスト
  • +
+

内部ではなく公開された動作に焦点を当てたテストの作成を強制するために、単体テストは別々のパッケージに配置しましょう。

+

ソースコード

+
    +
  • ユーティリティ関数
  • +
+

従来の if err != nil の代わりに *testing.T 変数を使用してエラーを処理すると、コードが短く、読みやすくなります。

+

ソースコード

+
    +
  • setup と teardown
  • +
+

setup および teardown 機能を利用して、統合テストの場合など、複雑な環境を構成できます。

+

ソースコード

+

ファジングを使用していない(community mistake)

+
+要約 +

ファジングは、複雑な関数やメソッドへのランダムな、予想外の、または不正な入力を検出し、脆弱性、バグ、さらには潜在的なクラッシュを発見するのに効率的です。

+
+

@jeromedoucet さんのご協力に感謝いたします。

+

最適化

+

CPU キャッシュを理解していない (#91)

+
    +
  • CPU アーキテクチャ
  • +
+

L1 キャッシュはメインメモリよりも約 50 ~ 100 倍高速であるため、CPU バウンドのアプリケーションを最適化するには、CPU キャッシュの使用方法を理解することが重要です。

+
    +
  • キャッシュライン
  • +
+

キャッシュラインの概念を意識することは、データ集約型アプリケーションでデータを整理する方法を理解するのに重要です。CPU はメモリをワードごとにフェッチしません。代わりに、通常はメモリブロックを 64 バイトのキャッシュラインにコピーします。個々のキャッシュラインを最大限に活用するには、空間的局所性を強制してください。

+

ソースコード

+
    +
  • 構造体のスライスとスライスの構造体
  • +
+ + +

ソースコード

+
    +
  • 予測可能性
  • +
+

CPU にとって予測可能なコードにすることは、特定の関数を最適化する効率的な方法でもあります。たとえば、ユニットまたは定数ストライドは CPU にとって予測可能ですが、非ユニットストライド(連結リストなど)は予測できません。

+

ソースコード

+
    +
  • キャッシュ配置ポリシー
  • +
+

キャッシュがパーティション化されていることを認識することで、重大なストライドを回避し、キャッシュのごく一部のみを使用するようにすることができます。

+

誤った共有を引き起こす並行処理(#92)

+
+要約 +

下位レベルの CPU キャッシュがすべてのコアで共有されるわけではないことを知っておくと、並行処理におけるの誤った共有などでパフォーマンスを低下させてしまうことを回避できます。メモリの共有はありえないのです。

+
+

ソースコード

+

命令レベルの並列性を考慮しない (#93)

+
+要約 +

命令レベルの並列性(ILP)を使用してコードの特定の部分を最適化し、CPU ができるだけ多くの命令を並列実行できるようにしましょう。主な手順の 1 つにデータハザードの特定があります。

+
+

ソースコード

+

データの配置を意識していない (#94)

+
+要約 +

Go言語では、基本型は各々のサイズに合わせて配置されることを覚えておくことで、ありがちな間違いを避けることができます。たとえば、構造体のフィールドをサイズで降順に再編成すると、構造体がよりコンパクトになる(メモリ割り当てが少なくなり、空間的局所性が向上する)可能性があることに留意してください。

+
+

ソースコード

+

ヒープとスタックの違いを理解していない (#95)

+
+要約 +

ヒープとスタックの基本的な違いを理解することも、Go アプリケーションを最適化する際には大切です。スタック割り当ては容易なのに対して、ヒープ割り当ては遅く、メモリのクリーンアップに GC を利用します。

+
+

ソースコード

+

割り当てを減らす方法がわかっていない( API の変更、コンパイラの最適化、および sync.Pool) (#96)

+
+要約 +

割り当てを減らすことも、Go アプリケーションを最適化する上で重要です。これは、共有を防ぐために API を慎重に設計する、一般的な Go コンパイラの最適化を理解する、sync.Pool を使用するなど、さまざまな方法で行うことができます。

+
+

ソースコード

+

インライン展開をしていない (#97)

+
+要約 +

ファストパスのインライン化手法を使用して、関数の呼び出しにかかる償却時間を効率的に削減しましょう。

+
+

Go言語の診断ツールを利用していない (#98)

+
+要約 +

プロファイリングと実行トレーサを利用して、アプリケーションのパフォーマンスと最適化すべき部分について理解しましょう。

+
+

セクション全文はこちら

+

GC の仕組みを理解していない (#99)

+
+要約 +

GC の調整方法を理解すると、突然の負荷の増加をより効率的に処理できるなど、さまざまな恩恵が得られます。

+
+

Docker と Kubernetes 上でGo言語を実行することの影響を理解していない (#100)

+
+要約 +

Docker と Kubernetes にデプロイする際の CPU スロットリングを回避するには、Go言語が CFS 対応ではないことに留意してください。

+
+ + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 00000000..2647aca0 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Common Go Mistakes","text":"

This page is a summary of the mistakes in the 100 Go Mistakes and How to Avoid Them book. Meanwhile, it's also open to the community. If you believe that a common Go mistake should be added, please create an issue.

Jobs

Is your company hiring? Sponsor this repository and let a significant audience of Go developers (~1k unique visitors per week) know about your opportunities in this section.

Beta

You're viewing a beta version enriched with significantly more content. However, this version is not yet complete, and I'm looking for volunteers to help me summarize the remaining mistakes (GitHub issue #43).

Progress:

"},{"location":"#code-and-project-organization","title":"Code and Project Organization","text":""},{"location":"#unintended-variable-shadowing-1","title":"Unintended variable shadowing (#1)","text":"TL;DR

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

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

Source code

"},{"location":"#unnecessary-nested-code-2","title":"Unnecessary nested code (#2)","text":"TL;DR

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

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

  • When an if block returns, we should omit the else block in all cases. For example, we shouldn\u2019t write:
if foo() {\n    // ...\n    return true\n} else {\n    // ...\n}\n

Instead, we omit the else block like this:

if foo() {\n    // ...\n    return true\n}\n// ...\n
  • We can also follow this logic with a non-happy path:
if s != \"\" {\n    // ...\n} else {\n    return errors.New(\"empty string\")\n}\n

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

if s == \"\" {\n    return errors.New(\"empty string\")\n}\n// ...\n

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\u2019s readability.

Source code

"},{"location":"#misusing-init-functions-3","title":"Misusing init functions (#3)","text":"TL;DR

When initializing variables, remember that init functions have limited error handling and make state handling and testing more complex. In most cases, initializations should be handled as specific functions.

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

Init functions can lead to some issues:

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

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

Source code

"},{"location":"#overusing-getters-and-setters-4","title":"Overusing getters and setters (#4)","text":"TL;DR

Forcing the use of getters and setters isn\u2019t idiomatic in Go. Being pragmatic and finding the right balance between efficiency and blindly following certain idioms should be the way to go.

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

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

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

"},{"location":"#interface-pollution-5","title":"Interface pollution (#5)","text":"TL;DR

Abstractions should be discovered, not created. To prevent unnecessary complexity, create an interface when you need it and not when you foresee needing it, or if you can at least prove the abstraction to be a valid one.

Read the full section here.

Source code

"},{"location":"#interface-on-the-producer-side-6","title":"Interface on the producer side (#6)","text":"TL;DR

Keeping interfaces on the client side avoids unnecessary abstractions.

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

An interface should live on the consumer side in most cases. However, in particular contexts (for example, when we know\u2014not foresee\u2014that 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

"},{"location":"#returning-interfaces-7","title":"Returning interfaces (#7)","text":"TL;DR

To prevent being restricted in terms of flexibility, a function shouldn\u2019t return interfaces but concrete implementations in most cases. Conversely, a function should accept interfaces whenever possible.

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

"},{"location":"#any-says-nothing-8","title":"any says nothing (#8)","text":"TL;DR

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

The any type can be helpful if there is a genuine need for accepting or returning any possible type (for instance, when it comes to marshaling or formatting). In general, we should avoid overgeneralizing the code we write at all costs. Perhaps a little bit of duplicated code might occasionally be better if it improves other aspects such as code expressiveness.

Source code

"},{"location":"#being-confused-about-when-to-use-generics-9","title":"Being confused about when to use generics (#9)","text":"TL;DR

Relying on generics and type parameters can prevent writing boilerplate code to factor out elements or behaviors. However, do not use type parameters prematurely, but only when you see a concrete need for them. Otherwise, they introduce unnecessary abstractions and complexity.

Read the full section here.

Source code

"},{"location":"#not-being-aware-of-the-possible-problems-with-type-embedding-10","title":"Not being aware of the possible problems with type embedding (#10)","text":"TL;DR

Using type embedding can also help avoid boilerplate code; however, ensure that doing so doesn\u2019t lead to visibility issues where some fields should have remained hidden.

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

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

type Foo struct {\n    Bar // Embedded field\n}\n\ntype Bar struct {\n    Baz int\n}\n

In the Foo struct, the Bar type is declared without an associated name; hence, it\u2019s an embedded field.

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

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

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

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

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

Source code

"},{"location":"#not-using-the-functional-options-pattern-11","title":"Not using the functional options pattern (#11)","text":"TL;DR

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

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

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

type options struct {\n  port *int\n}\n\ntype Option func(options *options) error\n\nfunc WithPort(port int) Option {\n  return func(options *options) error {\n    if port < 0 {\n    return errors.New(\"port should be positive\")\n  }\n  options.port = &port\n  return nil\n  }\n}\n\nfunc NewServer(addr string, opts ...Option) ( *http.Server, error) {\n  var options options\n  for _, opt := range opts {\n    err := opt(&options)\n    if err != nil {\n      return nil, err\n    }\n  }\n\n  // At this stage, the options struct is built and contains the config\n  // Therefore, we can implement our logic related to port configuration\n  var port int\n  if options.port == nil {\n    port = defaultHTTPPort\n  } else {\n      if *options.port == 0 {\n      port = randomPort()\n    } else {\n      port = *options.port\n    }\n  }\n\n  // ...\n}\n

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

"},{"location":"#project-misorganization-project-structure-and-package-organization-12","title":"Project misorganization (project structure and package organization) (#12)","text":"

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

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

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

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

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

Note

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

"},{"location":"#creating-utility-packages-13","title":"Creating utility packages (#13)","text":"TL;DR

Naming is a critical piece of application design. Creating packages such as common, util, and shared doesn\u2019t bring much value for the reader. Refactor such packages into meaningful and specific package names.

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

Source code

"},{"location":"#ignoring-package-name-collisions-14","title":"Ignoring package name collisions (#14)","text":"TL;DR

To avoid naming collisions between variables and packages, leading to confusion or perhaps even bugs, use unique names for each one. If this isn\u2019t feasible, use an import alias to change the qualifier to differentiate the package name from the variable name, or think of a better name.

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

"},{"location":"#missing-code-documentation-15","title":"Missing code documentation (#15)","text":"TL;DR

To help clients and maintainers understand your code\u2019s purpose, document exported elements.

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

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

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

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

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

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

"},{"location":"#not-using-linters-16","title":"Not using linters (#16)","text":"TL;DR

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

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

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

  • https://golang.org/cmd/vet\u2014A standard Go analyzer
  • https://github.com/kisielk/errcheck\u2014An error checker
  • https://github.com/fzipp/gocyclo\u2014A cyclomatic complexity analyzer
  • https://github.com/jgautheron/goconst\u2014A repeated string constants analyzer

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

  • https://golang.org/cmd/gofmt\u2014A standard Go code formatter
  • https://godoc.org/golang.org/x/tools/cmd/goimports\u2014A standard Go imports formatter

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

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

"},{"location":"#data-types","title":"Data Types","text":""},{"location":"#creating-confusion-with-octal-literals-17","title":"Creating confusion with octal literals (#17)","text":"TL;DR

When reading existing code, bear in mind that integer literals starting with 0 are octal numbers. Also, to improve readability, make octal integers explicit by prefixing them with 0o.

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

We should also note the other integer literal representations:

  • Binary\u2014Uses a 0b or 0B prefix (for example, 0b100 is equal to 4 in base 10)
  • Hexadecimal\u2014Uses an 0x or 0X prefix (for example, 0xF is equal to 15 in base 10)
  • Imaginary\u2014Uses an i suffix (for example, 3i)

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

Source code

"},{"location":"#neglecting-integer-overflows-18","title":"Neglecting integer overflows (#18)","text":"TL;DR

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

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

var counter int32 = math.MaxInt32 + 1\n
constant 2147483648 overflows int32\n

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

"},{"location":"#not-understanding-floating-points-19","title":"Not understanding floating-points (#19)","text":"TL;DR

Making floating-point comparisons within a given delta can ensure that your code is portable. When performing addition or subtraction, group the operations with a similar order of magnitude to favor accuracy. Also, perform multiplication and division before addition and subtraction.

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

For that, we\u2019ll look at a multiplication example:

var n float32 = 1.0001\nfmt.Println(n * n)\n

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

Because Go\u2019s float32 and float64 types are approximations, we have to bear a few rules in mind:

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

Source code

"},{"location":"#not-understanding-slice-length-and-capacity-20","title":"Not understanding slice length and capacity (#20)","text":"TL;DR

Understanding the difference between slice length and capacity should be part of a Go developer\u2019s core knowledge. The slice length is the number of available elements in the slice, whereas the slice capacity is the number of elements in the backing array.

Read the full section here.

Source code

"},{"location":"#inefficient-slice-initialization-21","title":"Inefficient slice initialization (#21)","text":"TL;DR

When creating a slice, initialize it with a given length or capacity if its length is already known. This reduces the number of allocations and improves performance.

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

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

Source code

"},{"location":"#being-confused-about-nil-vs-empty-slice-22","title":"Being confused about nil vs. empty slice (#22)","text":"TL;DR

To prevent common confusions such as when using the encoding/json or the reflect package, you need to understand the difference between nil and empty slices. Both are zero-length, zero-capacity slices, but only a nil slice doesn\u2019t require allocation.

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

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

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

Source code

"},{"location":"#not-properly-checking-if-a-slice-is-empty-23","title":"Not properly checking if a slice is empty (#23)","text":"TL;DR

To check if a slice doesn\u2019t contain any element, check its length. This check works regardless of whether the slice is nil or empty. The same goes for maps. To design unambiguous APIs, you shouldn\u2019t distinguish between nil and empty slices.

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

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

Source code

"},{"location":"#not-making-slice-copies-correctly-24","title":"Not making slice copies correctly (#24)","text":"TL;DR

To copy one slice to another using the copy built-in function, remember that the number of copied elements corresponds to the minimum between the two slice\u2019s lengths.

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

Source code

"},{"location":"#unexpected-side-effects-using-slice-append-25","title":"Unexpected side effects using slice append (#25)","text":"TL;DR

Using copy or the full slice expression is a way to prevent append from creating conflicts if two different functions use slices backed by the same array. However, only a slice copy prevents memory leaks if you want to shrink a large slice.

When using slicing, we must remember that we can face a situation leading to unintended side effects. If the resulting slice has a length smaller than its capacity, append can mutate the original slice. If we want to restrict the range of possible side effects, we can use either a slice copy or the full slice expression, which prevents us from doing a copy.

Note

s[low:high:max] (full slice expression): This statement creates a slice similar to the one created with s[low:high], except that the resulting slice\u2019s capacity is equal to max - low.

Source code

"},{"location":"#slices-and-memory-leaks-26","title":"Slices and memory leaks (#26)","text":"TL;DR

Working with a slice of pointers or structs with pointer fields, you can avoid memory leaks by marking as nil the elements excluded by a slicing operation.

"},{"location":"#leaking-capacity","title":"Leaking capacity","text":"

Remember that slicing a large slice or array can lead to potential high memory consumption. The remaining space won\u2019t 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

"},{"location":"#slice-and-pointers","title":"Slice and pointers","text":"

When we use the slicing operation with pointers or structs with pointer fields, we need to know that the GC won\u2019t 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

"},{"location":"#inefficient-map-initialization-27","title":"Inefficient map initialization (#27)","text":"TL;DR

When creating a map, initialize it with a given length if its length is already known. This reduces the number of allocations and improves performance.

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

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

Source code

"},{"location":"#maps-and-memory-leaks-28","title":"Maps and memory leaks (#28)","text":"TL;DR

A map can always grow in memory, but it never shrinks. Hence, if it leads to some memory issues, you can try different options, such as forcing Go to recreate the map or using pointers.

Read the full section here.

Source code

"},{"location":"#comparing-values-incorrectly-29","title":"Comparing values incorrectly (#29)","text":"TL;DR

To compare types in Go, you can use the == and != operators if two types are comparable: Booleans, numerals, strings, pointers, channels, and structs are composed entirely of comparable types. Otherwise, you can either use reflect.DeepEqual and pay the price of reflection or use custom implementations and libraries.

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

  • Booleans\u2014Compare whether two Booleans are equal.
  • Numerics (int, float, and complex types)\u2014Compare whether two numerics are equal.
  • Strings\u2014Compare whether two strings are equal.
  • Channels\u2014Compare whether two channels were created by the same call to make or if both are nil.
  • Interfaces\u2014Compare whether two interfaces have identical dynamic types and equal dynamic values or if both are nil.
  • Pointers\u2014Compare whether two pointers point to the same value in memory or if both are nil.
  • Structs and arrays\u2014Compare whether they are composed of similar types.
Note

We can also use the ?, >=, <, and > operators with numeric types to compare values and with strings to compare their lexical order.

If operands are not comparable (e.g., slices and maps), we have to use other options such as reflection. Reflection is a form of metaprogramming, and it refers to the ability of an application to introspect and modify its structure and behavior. For example, in Go, we can use reflect.DeepEqual. This function reports whether two elements are deeply equal by recursively traversing two values. The elements it accepts are basic types plus arrays, structs, slices, maps, pointers, interfaces, and functions. Yet, the main catch is the performance penalty.

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

Source code

"},{"location":"#control-structures","title":"Control Structures","text":""},{"location":"#ignoring-that-elements-are-copied-in-range-loops-30","title":"Ignoring that elements are copied in range loops (#30)","text":"TL;DR

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

A range loop allows iterating over different data structures:

  • String
  • Array
  • Pointer to an array
  • Slice
  • Map
  • Receiving channel

Compared to a classic for loop, a range loop is a convenient way to iterate over all the elements of one of these data structures, thanks to its concise syntax.

Yet, we should remember that the value element in a range loop is a copy. Therefore, if the value is a struct we need to mutate, we will only update the copy, not the element itself, unless the value or field we modify is a pointer. The favored options are to access the element via the index using a range loop or a classic for loop.

Source code

"},{"location":"#ignoring-how-arguments-are-evaluated-in-range-loops-channels-and-arrays-31","title":"Ignoring how arguments are evaluated in range loops (channels and arrays) (#31)","text":"TL;DR

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

The range loop evaluates the provided expression only once, before the beginning of the loop, by doing a copy (regardless of the type). We should remember this behavior to avoid common mistakes that might, for example, lead us to access the wrong element. For example:

a := [3]int{0, 1, 2}\nfor i, v := range a {\n    a[2] = 10\n    if i == 2 {\n        fmt.Println(v)\n    }\n}\n

This code updates the last index to 10. However, if we run this code, it does not print 10; it prints 2.

Source code

"},{"location":"#ignoring-the-impacts-of-using-pointer-elements-in-range-loops-32","title":"Ignoring the impacts of using pointer elements in range loops (#32)","text":"Warning

This mistake isn't relevant anymore from Go 1.22 (details).

"},{"location":"#making-wrong-assumptions-during-map-iterations-ordering-and-map-insert-during-iteration-33","title":"Making wrong assumptions during map iterations (ordering and map insert during iteration) (#33)","text":"TL;DR

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

  • Doesn\u2019t order the data by keys
  • Doesn\u2019t preserve the insertion order
  • Doesn\u2019t have a deterministic iteration order
  • Doesn\u2019t guarantee that an element added during an iteration will be produced during this iteration

Source code

"},{"location":"#ignoring-how-the-break-statement-works-34","title":"Ignoring how the break statement works (#34)","text":"TL;DR

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

A break statement is commonly used to terminate the execution of a loop. When loops are used in conjunction with switch or select, developers frequently make the mistake of breaking the wrong statement. For example:

for i := 0; i < 5; i++ {\n    fmt.Printf(\"%d \", i)\n\n    switch i {\n    default:\n    case 2:\n        break\n    }\n}\n

The break statement doesn\u2019t terminate the for loop: it terminates the switch statement, instead. Hence, instead of iterating from 0 to 2, this code iterates from 0 to 4: 0 1 2 3 4.

One essential rule to keep in mind is that a break statement terminates the execution of the innermost for, switch, or select statement. In the previous example, it terminates the switch statement.

To break the loop instead of the switch statement, the most idiomatic way is to use a label:

loop:\n    for i := 0; i < 5; i++ {\n        fmt.Printf(\"%d \", i)\n\n        switch i {\n        default:\n        case 2:\n            break loop\n        }\n    }\n

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

"},{"location":"#using-defer-inside-a-loop-35","title":"Using defer inside a loop (#35)","text":"TL;DR

Extracting loop logic inside a function leads to executing a defer statement at the end of each iteration.

The defer statement delays a call\u2019s execution until the surrounding function returns. It\u2019s mainly used to reduce boilerplate code. For example, if a resource has to be closed eventually, we can use defer to avoid repeating the closure calls before every single return.

One common mistake with defer is to forget that it schedules a function call when the surrounding function returns. For example:

func readFiles(ch <-chan string) error {\n    for path := range ch {\n        file, err := os.Open(path)\n        if err != nil {\n            return err\n        }\n\n        defer file.Close()\n\n        // Do something with file\n    }\n    return nil\n}\n

The defer calls are executed not during each loop iteration but when the readFiles function returns. If readFiles doesn\u2019t return, the file descriptors will be kept open forever, causing leaks.

One common option to fix this problem is to create a surrounding function after defer, called during each iteration:

func readFiles(ch <-chan string) error {\n    for path := range ch {\n        if err := readFile(path); err != nil {\n            return err\n        }\n    }\n    return nil\n}\n\nfunc readFile(path string) error {\n    file, err := os.Open(path)\n    if err != nil {\n        return err\n    }\n\n    defer file.Close()\n\n    // Do something with file\n    return nil\n}\n

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

"},{"location":"#strings","title":"Strings","text":""},{"location":"#not-understanding-the-concept-of-rune-36","title":"Not understanding the concept of rune (#36)","text":"TL;DR

Understanding that a rune corresponds to the concept of a Unicode code point and that it can be composed of multiple bytes should be part of the Go developer\u2019s core knowledge to work accurately with strings.

As runes are everywhere in Go, it's important to understand the following:

  • A charset is a set of characters, whereas an encoding describes how to translate a charset into binary.
  • In Go, a string references an immutable slice of arbitrary bytes.
  • Go source code is encoded using UTF-8. Hence, all string literals are UTF-8 strings. But because a string can contain arbitrary bytes, if it\u2019s obtained from somewhere else (not the source code), it isn\u2019t guaranteed to be based on the UTF-8 encoding.
  • A rune corresponds to the concept of a Unicode code point, meaning an item represented by a single value.
  • Using UTF-8, a Unicode code point can be encoded into 1 to 4 bytes.
  • Using len() on a string in Go returns the number of bytes, not the number of runes.

Source code

"},{"location":"#inaccurate-string-iteration-37","title":"Inaccurate string iteration (#37)","text":"TL;DR

Iterating on a string with the range operator iterates on the runes with the index corresponding to the starting index of the rune\u2019s byte sequence. To access a specific rune index (such as the third rune), convert the string into a []rune.

Iterating on a string is a common operation for developers. Perhaps we want to perform an operation for each rune in the string or implement a custom function to search for a specific substring. In both cases, we have to iterate on the different runes of a string. But it\u2019s easy to get confused about how iteration works.

For example, consider the following example:

s := \"h\u00eallo\"\nfor i := range s {\n    fmt.Printf(\"position %d: %c\\n\", i, s[i])\n}\nfmt.Printf(\"len=%d\\n\", len(s))\n
position 0: h\nposition 1: \u00c3\nposition 3: l\nposition 4: l\nposition 5: o\nlen=6\n

Let's highlight three points that might be confusing:

  • The second rune is \u00c3 in the output instead of \u00ea.
  • We jumped from position 1 to position 3: what is at position 2?
  • len returns a count of 6, whereas s contains only 5 runes.

Let\u2019s start with the last observation. We already mentioned that len returns the number of bytes in a string, not the number of runes. Because we assigned a string literal to s, s is a UTF-8 string. Meanwhile, the special character \"\u00ea\" isn\u2019t encoded in a single byte; it requires 2 bytes. Therefore, calling len(s) returns 6.

Meanwhile, in the previous example, we have to understand that we don't iterate over each rune; instead, we iterate over each starting index of a rune:

Printing s[i] doesn\u2019t print the ith rune; it prints the UTF-8 representation of the byte at index i. Hence, we printed \"h\u00c3llo\" instead of \"h\u00eallo\".

If we want to print all the different runes, we can either use the value element of the range operator:

s := \"h\u00eallo\"\nfor i, r := range s {\n    fmt.Printf(\"position %d: %c\\n\", i, r)\n}\n

Or, we can convert the string into a slice of runes and iterate over it:

s := \"h\u00eallo\"\nrunes := []rune(s)\nfor i, r := range runes {\n    fmt.Printf(\"position %d: %c\\n\", i, r)\n}\n

Note that this solution introduces a run-time overhead compared to the previous one. Indeed, converting a string into a slice of runes requires allocating an additional slice and converting the bytes into runes: an O(n) time complexity with n the number of bytes in the string. Therefore, if we want to iterate over all the runes, we should use the first solution.

However, if we want to access the ith rune of a string with the first option, we don\u2019t have access to the rune index; rather, we know the starting index of a rune in the byte sequence.

s := \"h\u00eallo\"\nr := []rune(s)[4]\nfmt.Printf(\"%c\\n\", r) // o\n

Source code

"},{"location":"#misusing-trim-functions-38","title":"Misusing trim functions (#38)","text":"TL;DR

strings.TrimRight/strings.TrimLeft removes all the trailing/leading runes contained in a given set, whereas strings.TrimSuffix/strings.TrimPrefix returns a string without a provided suffix/prefix.

For example:

fmt.Println(strings.TrimRight(\"123oxo\", \"xo\"))\n

The example prints 123:

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

"},{"location":"#under-optimized-strings-concatenation-39","title":"Under-optimized strings concatenation (#39)","text":"TL;DR

Concatenating a list of strings should be done with strings.Builder to prevent allocating a new string during each iteration.

Let\u2019s consider a concat function that concatenates all the string elements of a slice using the += operator:

func concat(values []string) string {\n    s := \"\"\n    for _, value := range values {\n        s += value\n    }\n    return s\n}\n

During each iteration, the += operator concatenates s with the value string. At first sight, this function may not look wrong. But with this implementation, we forget one of the core characteristics of a string: its immutability. Therefore, each iteration doesn\u2019t update s; it reallocates a new string in memory, which significantly impacts the performance of this function.

Fortunately, there is a solution to deal with this problem, using strings.Builder:

func concat(values []string) string {\n    sb := strings.Builder{}\n    for _, value := range values {\n        _, _ = sb.WriteString(value)\n    }\n    return sb.String()\n}\n

During each iteration, we constructed the resulting string by calling the WriteString method that appends the content of value to its internal buffer, hence minimizing memory copying.

Note

WriteString returns an error as the second output, but we purposely ignore it. Indeed, this method will never return a non-nil error. So what\u2019s the purpose of this method returning an error as part of its signature? strings.Builder implements the io.StringWriter interface, which contains a single method: WriteString(s string) (n int, err error). Hence, to comply with this interface, WriteString must return an error.

Internally, strings.Builder holds a byte slice. Each call to WriteString results in a call to append on this slice. There are two impacts. First, this struct shouldn\u2019t be used concurrently, as the calls to append would lead to race conditions. The second impact is something that we saw in mistake #21, \"Inefficient slice initialization\": if the future length of a slice is already known, we should preallocate it. For that purpose, strings.Builder exposes a method Grow(n int) to guarantee space for another n bytes:

func concat(values []string) string {\n    total := 0\n    for i := 0; i < len(values); i++ {\n        total += len(values[i])\n    }\n\n    sb := strings.Builder{}\n    sb.Grow(total) (2)\n    for _, value := range values {\n        _, _ = sb.WriteString(value)\n    }\n    return sb.String()\n}\n

Let\u2019s run a benchmark to compare the three versions (v1 using +=; v2 using strings.Builder{} without preallocation; and v3 using strings.Builder{} with preallocation). The input slice contains 1,000 strings, and each string contains 1,000 bytes:

BenchmarkConcatV1-4             16      72291485 ns/op\nBenchmarkConcatV2-4           1188        878962 ns/op\nBenchmarkConcatV3-4           5922        190340 ns/op\n

As we can see, the latest version is by far the most efficient: 99% faster than v1 and 78% faster than v2.

strings.Builder is the recommended solution to concatenate a list of strings. Usually, this solution should be used within a loop. Indeed, if we just have to concatenate a few strings (such as a name and a surname), using strings.Builder is not recommended as doing so will make the code a bit less readable than using the += operator or fmt.Sprintf.

Source code

"},{"location":"#useless-string-conversions-40","title":"Useless string conversions (#40)","text":"TL;DR

Remembering that the bytes package offers the same operations as the strings package can help avoid extra byte/string conversions.

When choosing to work with a string or a []byte, most programmers tend to favor strings for convenience. But most I/O is actually done with []byte. For example, io.Reader, io.Writer, and io.ReadAll work with []byte, not strings.

When we\u2019re wondering whether we should work with strings or []byte, let\u2019s recall that working with []byte isn\u2019t 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\u2019re 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

"},{"location":"#substring-and-memory-leaks-41","title":"Substring and memory leaks (#41)","text":"TL;DR

Using copies instead of substrings can prevent memory leaks, as the string returned by a substring operation will be backed by the same byte array.

In mistake #26, \u201cSlices and memory leaks,\u201d we saw how slicing a slice or array may lead to memory leak situations. This principle also applies to string and substring operations.

We need to keep two things in mind while using the substring operation in Go. First, the interval provided is based on the number of bytes, not the number of runes. Second, a substring operation may lead to a memory leak as the resulting substring will share the same backing array as the initial string. The solutions to prevent this case from happening are to perform a string copy manually or to use strings.Clone from Go 1.18.

Source code

"},{"location":"#functions-and-methods","title":"Functions and Methods","text":""},{"location":"#not-knowing-which-type-of-receiver-to-use-42","title":"Not knowing which type of receiver to use (#42)","text":"TL;DR

The decision whether to use a value or a pointer receiver should be made based on factors such as the type, whether it has to be mutated, whether it contains a field that can\u2019t be copied, and how large the object is. When in doubt, use a pointer receiver.

Choosing between value and pointer receivers isn\u2019t always straightforward. Let\u2019s discuss some of the conditions to help us choose.

A receiver must be a pointer

  • If the method needs to mutate the receiver. This rule is also valid if the receiver is a slice and a method needs to append elements:
type slice []int\n\nfunc (s *slice) add(element int) {\n    *s = append(*s, element)\n}\n
  • If the method receiver contains a field that cannot be copied: for example, a type part of the sync package (see #74, \u201cCopying a sync type\u201d).

A receiver should be a pointer

  • If the receiver is a large object. Using a pointer can make the call more efficient, as doing so prevents making an extensive copy. When in doubt about how large is large, benchmarking can be the solution; it\u2019s pretty much impossible to state a specific size, because it depends on many factors.

A receiver must be a value

  • If we have to enforce a receiver\u2019s immutability.
  • If the receiver is a map, function, or channel. Otherwise, a compilation error occurs.

A receiver should be a value

  • If the receiver is a slice that doesn\u2019t have to be mutated.
  • If the receiver is a small array or struct that is naturally a value type without mutable fields, such as time.Time.
  • If the receiver is a basic type such as int, float64, or string.

Of course, it\u2019s impossible to be exhaustive, as there will always be edge cases, but this section\u2019s goal was to provide guidance to cover most cases. By default, we can choose to go with a value receiver unless there\u2019s a good reason not to do so. In doubt, we should use a pointer receiver.

Source code

"},{"location":"#never-using-named-result-parameters-43","title":"Never using named result parameters (#43)","text":"TL;DR

Using named result parameters can be an efficient way to improve the readability of a function/method, especially if multiple result parameters have the same type. In some cases, this approach can also be convenient because named result parameters are initialized to their zero value. But be cautious about potential side effects.

When we return parameters in a function or a method, we can attach names to these parameters and use them as regular variables. When a result parameter is named, it\u2019s initialized to its zero value when the function/method begins. With named result parameters, we can also call a naked return statement (without arguments). In that case, the current values of the result parameters are used as the returned values.

Here\u2019s an example that uses a named result parameter b:

func f(a int) (b int) {\n    b = a\n    return\n}\n

In this example, we attach a name to the result parameter: b. When we call return without arguments, it returns the current value of b.

In some cases, named result parameters can also increase readability: for example, if two parameters have the same type. In other cases, they can also be used for convenience. Therefore, we should use named result parameters sparingly when there\u2019s a clear benefit.

Source code

"},{"location":"#unintended-side-effects-with-named-result-parameters-44","title":"Unintended side effects with named result parameters (#44)","text":"TL;DR

See #43.

We mentioned why named result parameters can be useful in some situations. But as these result parameters are initialized to their zero value, using them can sometimes lead to subtle bugs if we\u2019re not careful enough. For example, can you spot what\u2019s wrong with this code?

func (l loc) getCoordinates(ctx context.Context, address string) (\n    lat, lng float32, err error) {\n    isValid := l.validateAddress(address) (1)\n    if !isValid {\n        return 0, 0, errors.New(\"invalid address\")\n    }\n\n    if ctx.Err() != nil { (2)\n        return 0, 0, err\n    }\n\n    // Get and return coordinates\n}\n

The error might not be obvious at first glance. Here, the error returned in the if ctx.Err() != nil scope is err. But we haven\u2019t assigned any value to the err variable. It\u2019s still assigned to the zero value of an error type: nil. Hence, this code will always return a nil error.

When using named result parameters, we must recall that each parameter is initialized to its zero value. As we have seen in this section, this can lead to subtle bugs that aren\u2019t always straightforward to spot while reading code. Therefore, let\u2019s remain cautious when using named result parameters, to avoid potential side effects.

Source code

"},{"location":"#returning-a-nil-receiver-45","title":"Returning a nil receiver (#45)","text":"TL;DR

When returning an interface, be cautious about not returning a nil pointer but an explicit nil value. Otherwise, unintended consequences may occur and the caller will receive a non-nil value.

Source code

"},{"location":"#using-a-filename-as-a-function-input-46","title":"Using a filename as a function input (#46)","text":"TL;DR

Designing functions to receive io.Reader types instead of filenames improves the reusability of a function and makes testing easier.

Accepting a filename as a function input to read from a file should, in most cases, be considered a code smell (except in specific functions such as os.Open). Indeed, it makes unit tests more complex because we may have to create multiple files. It also reduces the reusability of a function (although not all functions are meant to be reused). Using the io.Reader interface abstracts the data source. Regardless of whether the input is a file, a string, an HTTP request, or a gRPC request, the implementation can be reused and easily tested.

Source code

"},{"location":"#ignoring-how-defer-arguments-and-receivers-are-evaluated-argument-evaluation-pointer-and-value-receivers-47","title":"Ignoring how defer arguments and receivers are evaluated (argument evaluation, pointer, and value receivers) (#47)","text":"TL;DR

Passing a pointer to a defer function and wrapping a call inside a closure are two possible solutions to overcome the immediate evaluation of arguments and receivers.

In a defer function the arguments are evaluated right away, not once the surrounding function returns. For example, in this code, we always call notify and incrementCounter with the same status: an empty string.

const (\n    StatusSuccess  = \"success\"\n    StatusErrorFoo = \"error_foo\"\n    StatusErrorBar = \"error_bar\"\n)\n\nfunc f() error {\n    var status string\n    defer notify(status)\n    defer incrementCounter(status)\n\n    if err := foo(); err != nil {\n        status = StatusErrorFoo\n        return err\n    }\n\n    if err := bar(); err != nil {\n        status = StatusErrorBar\n        return err\n    }\n\n    status = StatusSuccess\n    return nil\n}\n

Indeed, we call notify(status) and incrementCounter(status) as defer functions. Therefore, Go will delay these calls to be executed once f returns with the current value of status at the stage we used defer, hence passing an empty string.

Two leading options if we want to keep using defer.

The first solution is to pass a string pointer:

func f() error {\n    var status string\n    defer notify(&status) \n    defer incrementCounter(&status)\n\n    // The rest of the function unchanged\n}\n

Using defer evaluates the arguments right away: here, the address of status. Yes, status itself is modified throughout the function, but its address remains constant, regardless of the assignments. Hence, if notify or incrementCounter uses the value referenced by the string pointer, it will work as expected. But this solution requires changing the signature of the two functions, which may not always be possible.

There\u2019s another solution: calling a closure (an anonymous function value that references variables from outside its body) as a defer statement:

func f() error {\n    var status string\n    defer func() {\n        notify(status)\n        incrementCounter(status)\n    }()\n\n    // The rest of the function unchanged\n}\n

Here, we wrap the calls to both notify and incrementCounter within a closure. This closure references the status variable from outside its body. Therefore, status is evaluated once the closure is executed, not when we call defer. This solution also works and doesn\u2019t require notify and incrementCounter to change their signature.

Let's also note this behavior applies with method receiver: the receiver is evaluated immediately.

Source code

"},{"location":"#error-management","title":"Error Management","text":""},{"location":"#panicking-48","title":"Panicking (#48)","text":"TL;DR

Using panic is an option to deal with errors in Go. However, it should only be used sparingly in unrecoverable conditions: for example, to signal a programmer error or when you fail to load a mandatory dependency.

In Go, panic is a built-in function that stops the ordinary flow:

func main() {\n    fmt.Println(\"a\")\n    panic(\"foo\")\n    fmt.Println(\"b\")\n}\n

This code prints a and then stops before printing b:

a\npanic: foo\n\ngoroutine 1 [running]:\nmain.main()\n        main.go:7 +0xb3\n

Panicking in Go should be used sparingly. There are two prominent cases, one to signal a programmer error (e.g., sql.Register that panics if the driver is nil or has already been register) and another where our application fails to create a mandatory 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

"},{"location":"#ignoring-when-to-wrap-an-error-49","title":"Ignoring when to wrap an error (#49)","text":"TL;DR

Wrapping an error allows you to mark an error and/or provide additional context. However, error wrapping creates potential coupling as it makes the source error available for the caller. If you want to prevent that, don\u2019t use error wrapping.

Since Go 1.13, the %w directive allows us to wrap errors conveniently. Error wrapping is about wrapping or packing an error inside a wrapper container that also makes the source error available. In general, the two main use cases for error wrapping are the following:

  • Adding additional context to an error
  • Marking an error as a specific error

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\u2019t 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\u2019t use error wrapping but error transformation, for example, using fmt.Errorf with the %v directive.

Source code

"},{"location":"#comparing-an-error-type-inaccurately-50","title":"Comparing an error type inaccurately (#50)","text":"TL;DR

If you use Go 1.13 error wrapping with the %w directive and fmt.Errorf, comparing an error against a type has to be done using errors.As. Otherwise, if the returned error you want to check is wrapped, it will fail the checks.

Source code

"},{"location":"#comparing-an-error-value-inaccurately-51","title":"Comparing an error value inaccurately (#51)","text":"TL;DR

If you use Go 1.13 error wrapping with the %w directive and fmt.Errorf, comparing an error against or a value has to be done using errors.As. Otherwise, if the returned error you want to check is wrapped, it will fail the checks.

A sentinel error is an error defined as a global variable:

import \"errors\"\n\nvar ErrFoo = errors.New(\"foo\")\n

In general, the convention is to start with Err followed by the error type: here, ErrFoo. A sentinel error conveys an expected error, an error that clients will expect to check. As general guidelines:

  • Expected errors should be designed as error values (sentinel errors): var ErrFoo = errors.New(\"foo\").
  • Unexpected errors should be designed as error types: type BarError struct { ... }, with BarError implementing the error interface.

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

"},{"location":"#handling-an-error-twice-52","title":"Handling an error twice (#52)","text":"TL;DR

In most situations, an error should be handled only once. Logging an error is handling an error. Therefore, you have to choose between logging or returning an error. In many cases, error wrapping is the solution as it allows you to provide additional context to an error and return the source error.

Handling an error multiple times is a mistake made frequently by developers, not specifically in Go. This can cause situations where the same error is logged multiple times make debugging harder.

Let's remind 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 wrapping is the most convenient approach as it allows us to propagate the source error and add context to an error.

Source code

"},{"location":"#not-handling-an-error-53","title":"Not handling an error (#53)","text":"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

"},{"location":"#not-handling-defer-errors-54","title":"Not handling defer errors (#54)","text":"TL;DR

In many cases, you shouldn\u2019t ignore an error returned by a defer function. Either handle it directly or propagate it to the caller, depending on the context. If you want to ignore it, use the blank identifier.

Consider the following code:

func f() {\n  // ...\n  notify() // Error handling is omitted\n}\n\nfunc notify() error {\n  // ...\n}\n

From a maintainability perspective, the code can lead to some issues. Let\u2019s consider a new reader looking at it. This reader notices that notify returns an error but that the error isn\u2019t handled by the parent function. How can they guess whether or not handling the error was intentional? How can they know whether the previous developer forgot to handle it or did it purposely?

For these reasons, when we want to ignore an error, there's only one way to do it, using the blank identifier (_):

_ = notify\n

In terms of compilation and run time, this approach doesn\u2019t change anything compared to the first piece of code. But this new version makes explicit that we aren\u2019t interested in the error. Also, we can add a comment that indicates the rationale for why an error is ignored:

// At-most once delivery.\n// Hence, it's accepted to miss some of them in case of errors.\n_ = notify()\n

Source code

"},{"location":"#concurrency-foundations","title":"Concurrency: Foundations","text":""},{"location":"#mixing-up-concurrency-and-parallelism-55","title":"Mixing up concurrency and parallelism (#55)","text":"TL;DR

Understanding the fundamental differences between concurrency and parallelism is a cornerstone of the Go developer\u2019s knowledge. Concurrency is about structure, whereas parallelism is about execution.

Concurrency and parallelism are not the same:

  • Concurrency is about structure. We can change a sequential implementation into a concurrent one by introducing different steps that separate concurrent goroutines can tackle.
  • Meanwhile, parallelism is about execution. We can use parallism at the steps level by adding more parallel goroutines.

In summary, concurrency provides a structure to solve a problem with parts that may be parallelized. Therefore, concurrency enables parallelism.

"},{"location":"#thinking-concurrency-is-always-faster-56","title":"Thinking concurrency is always faster (#56)","text":"TL;DR

To be a proficient developer, you must acknowledge that concurrency isn\u2019t 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.

Read the full section here.

Source code

"},{"location":"#being-puzzled-about-when-to-use-channels-or-mutexes-57","title":"Being puzzled about when to use channels or mutexes (#57)","text":"TL;DR

Being aware of goroutine interactions can also be helpful when deciding between channels and mutexes. In general, parallel goroutines require synchronization and hence mutexes. Conversely, concurrent goroutines generally require coordination and orchestration and hence channels.

Given a concurrency problem, it may not always be clear whether we can implement a solution using channels or mutexes. Because Go promotes sharing memory by communication, one mistake could be to always force the use of channels, regardless of the use case. However, we should see the two options as complementary.

When should we use channels or mutexes? We will use the example in the next figure as a backbone. Our example has three different goroutines with specific relationships:

  • G1 and G2 are parallel goroutines. They may be two goroutines executing the same function that keeps receiving messages from a channel, or perhaps two goroutines executing the same HTTP handler at the same time.
  • On the other hand, G1 and G3 are concurrent goroutines, as are G2 and G3. All the goroutines are part of an overall concurrent structure, but G1 and G2 perform the first step, whereas G3 does the next step.

In general, parallel goroutines have to synchronize: for example, when they need to access or mutate a shared resource such as a slice. Synchronization is enforced with mutexes but not with any channel types (not with buffered channels). Hence, in general, synchronization between parallel goroutines should be achieved via mutexes.

Conversely, in general, concurrent goroutines have to coordinate and orchestrate. For example, if G3 needs to aggregate results from both G1 and G2, G1 and G2 need to signal to G3 that a new intermediate result is available. This coordination falls under the scope of communication\u2014therefore, channels.

Regarding concurrent goroutines, there\u2019s also the case where we want to transfer the ownership of a resource from one step (G1 and G2) to another (G3); for example, if G1 and G2 are enriching a shared resource and at some point, we consider this job as complete. Here, we should use channels to signal that a specific resource is ready and handle the ownership transfer.

Mutexes and channels have different semantics. Whenever we want to share a state or access a shared resource, mutexes ensure exclusive access to this resource. Conversely, channels are a mechanic for signaling with or without data (chan struct{} or not). Coordination or ownership transfer should be achieved via channels. It\u2019s important to know whether goroutines are parallel or concurrent because, in general, we need mutexes for parallel goroutines and channels for concurrent ones.

"},{"location":"#not-understanding-race-problems-data-races-vs-race-conditions-and-the-go-memory-model-58","title":"Not understanding race problems (data races vs. race conditions and the Go memory model) (#58)","text":"TL;DR

Being proficient in concurrency also means understanding that data races and race conditions are different concepts. Data races occur when multiple goroutines simultaneously access the same memory location and at least one of them is writing. Meanwhile, being data-race-free doesn\u2019t necessarily mean deterministic execution. When a behavior depends on the sequence or the timing of events that can\u2019t be controlled, this is a race condition.

Race problems can be among the hardest and most insidious bugs a programmer can face. As Go developers, we must understand crucial aspects such as data races and race conditions, their possible impacts, and how to avoid them.

"},{"location":"#data-race","title":"Data Race","text":"

A data race occurs when two or more goroutines simultaneously access the same memory location and at least one is writing. In this case, the result can be hazardous. Even worse, in some situations, the memory location may end up holding a value containing a meaningless combination of bits.

We can prevent a data race from happening using different techniques. For example:

  • Using the sync/atomic package
  • In synchronizing the two goroutines with an ad hoc data structure like a mutex
  • Using channels to make the two goroutines communicating to ensure that a variable is updated by only one goroutine at a time
"},{"location":"#race-condition","title":"Race Condition","text":"

Depending on the operation we want to perform, does a data-race-free application necessarily mean a deterministic result? Not necessarily.

A race condition occurs when the behavior depends on the sequence or the timing of events that can\u2019t be controlled. Here, the timing of events is the goroutines\u2019 execution order.

In summary, when we work in concurrent applications, it\u2019s essential to understand that a data race is different from a race condition. A data race occurs when multiple goroutines simultaneously access the same memory location and at least one of them is writing. A data race means unexpected behavior. However, a data-race-free application doesn\u2019t necessarily mean deterministic results. An application can be free of data races but still have behavior that depends on uncontrolled events (such as goroutine execution, how fast a message is published to a channel, or how long a call to a database lasts); this is a race condition. Understanding both concepts is crucial to becoming proficient in designing concurrent applications.

Source code

"},{"location":"#not-understanding-the-concurrency-impacts-of-a-workload-type-59","title":"Not understanding the concurrency impacts of a workload type (#59)","text":"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.

In programming, the execution time of a workload is limited by one of the following:

  • The speed of the CPU\u2014For example, running a merge sort algorithm. The workload is called CPU-bound.
  • The speed of I/O\u2014For example, making a REST call or a database query. The workload is called I/O-bound.
  • The amount of available memory\u2014The workload is called memory-bound.
Note

The last is the rarest nowadays, given that memory has become very cheap in recent decades. Hence, this section focuses on the two first workload types: CPU- and I/O-bound.

If the workload executed by the workers is I/O-bound, the value mainly depends on the external system. Conversely, if the workload is CPU-bound, the optimal number of goroutines is close to the number of available CPU cores (a best practice can be to use runtime.GOMAXPROCS). Knowing the workload type (I/O or CPU) is crucial when designing concurrent applications.

Source code

"},{"location":"#misunderstanding-go-contexts-60","title":"Misunderstanding Go contexts (#60)","text":"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.

https://pkg.go.dev/context

A Context carries a deadline, a cancellation signal, and other values across API boundaries.

"},{"location":"#deadline","title":"Deadline","text":"

A deadline refers to a specific point in time determined with one of the following:

  • A time.Duration from now (for example, in 250 ms)
  • A time.Time (for example, 2023-02-07 00:00:00 UTC)

The semantics of a deadline convey that an ongoing activity should be stopped if this deadline is met. An activity is, for example, an I/O request or a goroutine waiting to receive a message from a channel.

"},{"location":"#cancellation-signals","title":"Cancellation signals","text":"

Another use case for Go contexts is to carry a cancellation signal. Let\u2019s imagine that we want to create an application that calls CreateFileWatcher(ctx context.Context, filename string) within another goroutine. This function creates a specific file watcher that keeps reading from a file and catches updates. When the provided context expires or is canceled, this function handles it to close the file descriptor.

"},{"location":"#context-values","title":"Context values","text":"

The last use case for Go contexts is to carry a key-value list. What\u2019s the point of having a context carrying a key-value list? Because Go contexts are generic and mainstream, there are infinite use cases.

For example, if we use tracing, we may want different subfunctions to share the same correlation ID. Some developers may consider this ID too invasive to be part of the function signature. In this regard, we could also decide to include it as part of the provided context.

"},{"location":"#catching-a-context-cancellation","title":"Catching a context cancellation","text":"

The context.Context type exports a Done method that returns a receive-only notification channel: <-chan struct{}. This channel is closed when the work associated with the context should be canceled. For example,

  • The Done channel related to a context created with context.WithCancel is closed when the cancel function is called.
  • The Done channel related to a context created with context.WithDeadline is closed when the deadline has expired.

One thing to note is that the internal channel should be closed when a context is canceled or has met a deadline, instead of when it receives a specific value, because the closure of a channel is the only channel action that all the consumer goroutines will receive. This way, all the consumers will be notified once a context is canceled or a deadline is reached.

In summary, to be a proficient Go developer, we have to understand what a context is and how to use it. In general, a function that users wait for should take a context, as doing so allows upstream callers to decide when calling this function should be aborted.

Source code

"},{"location":"#concurrency-practice","title":"Concurrency: Practice","text":""},{"location":"#propagating-an-inappropriate-context-61","title":"Propagating an inappropriate context (#61)","text":"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.

In many situations, it is recommended to propagate Go contexts. However, context propagation can sometimes lead to subtle bugs, preventing subfunctions from being correctly executed.

Let\u2019s consider the following example. We expose an HTTP handler that performs some tasks and returns a response. But just before returning the response, we also want to send it to a Kafka topic. We don\u2019t want to penalize the HTTP consumer latency-wise, so we want the publish action to be handled asynchronously within a new goroutine. We assume that we have at our disposal a publish function that accepts a context so the action of publishing a message can be interrupted if the context is canceled, for example. Here is a possible implementation:

func handler(w http.ResponseWriter, r *http.Request) {\n    response, err := doSomeTask(r.Context(), r)\n    if err != nil {\n        http.Error(w, err.Error(), http.StatusInternalServerError)\n    return\n    }\n    go func() {\n        err := publish(r.Context(), response)\n        // Do something with err\n    }()\n    writeResponse(response)\n}\n

What\u2019s wrong with this piece of code? We have to know that the context attached to an HTTP request can cancel in different conditions:

  • When the client\u2019s connection closes
  • In the case of an HTTP/2 request, when the request is canceled
  • When the response has been written back to the client

In the first two cases, we probably handle things correctly. For example, if we get a response from doSomeTask but the client has closed the connection, it\u2019s probably OK to call publish with a context already canceled so the message isn\u2019t published. But what about the last case?

When the response has been written to the client, the context associated with the request will be canceled. Therefore, we are facing a race condition:

  • If the response is written after the Kafka publication, we both return a response and publish a message successfully
  • However, if the response is written before or during the Kafka publication, the message shouldn\u2019t be published.

In the latter case, calling publish will return an error because we returned the HTTP response quickly.

Note

From Go 1.21, there is a way to create a new context without cancel. context.WithoutCancel returns a copy of parent that is not canceled when parent is canceled.

In summary, propagating a context should be done cautiously.

Source code

"},{"location":"#starting-a-goroutine-without-knowing-when-to-stop-it-62","title":"Starting a goroutine without knowing when to stop it (#62)","text":"TL;DR

Avoiding leaks means being mindful that whenever a goroutine is started, you should have a plan to stop it eventually.

Goroutines are easy and cheap to start\u2014so easy and cheap that we may not necessarily have a plan for when to stop a new goroutine, which can lead to leaks. Not knowing when to stop a goroutine is a design issue and a common concurrency mistake in Go.

Let\u2019s discuss a concrete example. We will design an application that needs to watch some external configuration (for example, using a database connection). Here\u2019s a first implementation:

func main() {\n    newWatcher()\n    // Run the application\n}\n\ntype watcher struct { /* Some resources */ }\n\nfunc newWatcher() {\n    w := watcher{}\n    go w.watch() // Creates a goroutine that watches some external configuration\n}\n

The problem with this code is that when the main goroutine exits (perhaps because of an OS signal or because it has a finite workload), the application is stopped. Hence, the resources created by watcher aren\u2019t closed gracefully. How can we prevent this from happening?

One option could be to pass to newWatcher a context that will be canceled when main returns:

func main() {\n    ctx, cancel := context.WithCancel(context.Background())\n    defer cancel()\n    newWatcher(ctx)\n    // Run the application\n}\n\nfunc newWatcher(ctx context.Context) {\n    w := watcher{}\n    go w.watch(ctx)\n}\n

We propagate the context created to the watch method. When the context is canceled, the watcher struct should close its resources. However, can we guarantee that watch will have time to do so? Absolutely not\u2014and that\u2019s a design flaw.

The problem is that we used signaling to convey that a goroutine had to be stopped. We didn\u2019t block the parent goroutine until the resources had been closed. Let\u2019s make sure we do:

func main() {\n    w := newWatcher()\n    defer w.close()\n    // Run the application\n}\n\nfunc newWatcher() watcher {\n    w := watcher{}\n    go w.watch()\n    return w\n}\n\nfunc (w watcher) close() {\n    // Close the resources\n}\n

Instead of signaling watcher that it\u2019s time to close its resources, we now call this close method, using defer to guarantee that the resources are closed before the application exits.

In summary, let\u2019s be mindful that a goroutine is a resource like any other that must eventually be closed to free memory or other resources. Starting a goroutine without knowing when to stop it is a design issue. Whenever a goroutine is started, we should have a clear plan about when it will stop. Last but not least, if a goroutine creates resources and its lifetime is bound to the lifetime of the application, it\u2019s probably safer to wait for this goroutine to complete before exiting the application. This way, we can ensure that the resources can be freed.

Source code

"},{"location":"#not-being-careful-with-goroutines-and-loop-variables-63","title":"Not being careful with goroutines and loop variables (#63)","text":"Warning

This mistake isn't relevant anymore from Go 1.22 (details).

"},{"location":"#expecting-a-deterministic-behavior-using-select-and-channels-64","title":"Expecting a deterministic behavior using select and channels (#64)","text":"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.

One common mistake made by Go developers while working with channels is to make wrong assumptions about how select behaves with multiple channels.

For example, let's consider the following case (disconnectCh is a unbuffered channel):

go func() {\n  for i := 0; i < 10; i++ {\n      messageCh <- i\n    }\n    disconnectCh <- struct{}{}\n}()\n\nfor {\n    select {\n    case v := <-messageCh:\n        fmt.Println(v)\n    case <-disconnectCh:\n        fmt.Println(\"disconnection, return\")\n        return\n    }\n}\n

If we run this example multiple times, the result will be random:

0\n1\n2\ndisconnection, return\n\n0\ndisconnection, return\n

Instead of consuming the 10 messages, we only received a few of them. What\u2019s the reason? It lies in the specification of the select statement with multiple channels (https:// go.dev/ref/spec):

Quote

If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection.

Unlike a switch statement, where the first case with a match wins, the select statement selects randomly if multiple options are possible.

This behavior might look odd at first, but there\u2019s a good reason for it: to prevent possible starvation. Suppose the first possible communication chosen is based on the source order. In that case, we may fall into a situation where, for example, we only receive from one channel because of a fast sender. To prevent this, the language designers decided to use a random selection.

When using select with multiple channels, we must remember that if multiple options are possible, the first case in the source order does not automatically win. Instead, Go selects randomly, so there\u2019s no guarantee about which option will be chosen. To overcome this behavior, in the case of a single producer goroutine, we can use either unbuffered channels or a single channel.

Source code

"},{"location":"#not-using-notification-channels-65","title":"Not using notification channels (#65)","text":"TL;DR

Send notifications using a chan struct{} type.

Channels are a mechanism for communicating across goroutines via signaling. A signal can be either with or without data.

Let\u2019s look at a concrete example. We will create a channel that will notify us whenever a certain disconnection occurs. One idea is to handle it as a chan bool:

disconnectCh := make(chan bool)\n

Now, let\u2019s say we interact with an API that provides us with such a channel. Because it\u2019s a channel of Booleans, we can receive either true or false messages. It\u2019s probably clear what true conveys. But what does false mean? Does it mean we haven\u2019t been disconnected? And in this case, how frequently will we receive such a signal? Does it mean we have reconnected? Should we even expect to receive false? Perhaps we should only expect to receive true messages.

If that\u2019s the case, meaning we don\u2019t need a specific value to convey some information, we need a channel without data. The idiomatic way to handle it is a channel of empty structs: chan struct{}.

"},{"location":"#not-using-nil-channels-66","title":"Not using nil channels (#66)","text":"TL;DR

Using nil channels should be part of your concurrency toolset because it allows you to remove cases from select statements, for example.

What should this code do?

var ch chan int\n<-ch\n

ch is a chan int type. The zero value of a channel being nil, ch is nil. The goroutine won\u2019t panic; however, it will block forever.

The principle is the same if we send a message to a nil channel. This goroutine blocks forever:

var ch chan int\nch <- 0\n

Then what\u2019s the purpose of Go allowing messages to be received from or sent to a nil channel? For example, we can use nil channels to implement an idiomatic way to merge two channels:

func merge(ch1, ch2 <-chan int) <-chan int {\n    ch := make(chan int, 1)\n\n    go func() {\n        for ch1 != nil || ch2 != nil { // Continue if at least one channel isn\u2019t nil\n            select {\n            case v, open := <-ch1:\n                if !open {\n                    ch1 = nil // Assign ch1 to a nil channel once closed\n                    break\n                }\n                ch <- v\n            case v, open := <-ch2:\n                if !open {\n                    ch2 = nil // Assigns ch2 to a nil channel once closed\n                    break\n                }\n                ch <- v\n            }\n        }\n        close(ch)\n    }()\n\n    return ch\n}\n

This elegant solution relies on nil channels to somehow remove one case from the select statement.

Let\u2019s keep this idea in mind: nil channels are useful in some conditions and should be part of the Go developer\u2019s toolset when dealing with concurrent code.

Source code

"},{"location":"#being-puzzled-about-channel-size-67","title":"Being puzzled about channel size (#67)","text":"TL;DR

Carefully decide on the right channel type to use, given a problem. Only unbuffered channels provide strong synchronization guarantees. For buffered channels, you should have a good reason to specify a channel size other than one.

An unbuffered channel is a channel without any capacity. It can be created by either omitting the size or providing a 0 size:

ch1 := make(chan int)\nch2 := make(chan int, 0)\n

With an unbuffered channel (sometimes called a synchronous channel), the sender will block until the receiver receives data from the channel.

Conversely, a buffered channel has a capacity, and it must be created with a size greater than or equal to 1:

ch3 := make(chan int, 1)\n

With a buffered channel, a sender can send messages while the channel isn\u2019t full. Once the channel is full, it will block until a receiver goroutine receives a message:

ch3 := make(chan int, 1)\nch3 <-1 // Non-blocking\nch3 <-2 // Blocking\n

The first send isn\u2019t blocking, whereas the second one is, as the channel is full at this stage.

What's the main difference between unbuffered and buffered channels:

  • An unbuffered channel enables synchronization. We have the guarantee that two goroutines will be in a known state: one receiving and another sending a message.
  • A buffered channel doesn\u2019t provide any strong synchronization. Indeed, a producer goroutine can send a message and then continue its execution if the channel isn\u2019t full. The only guarantee is that a goroutine won\u2019t receive a message before it is sent. But this is only a guarantee because of causality (you don\u2019t drink your coffee before you prepare it).

If we need a buffered channel, what size should we provide?

The default value we should use for buffered channels is its minimum: 1. So, we may approach the problem from this standpoint: is there any good reason not to use a value of 1? Here\u2019s a list of possible cases where we should use another size:

  • While using a worker pooling-like pattern, meaning spinning a fixed number of goroutines that need to send data to a shared channel. In that case, we can tie the channel size to the number of goroutines created.
  • When using channels for rate-limiting problems. For example, if we need to enforce resource utilization by bounding the number of requests, we should set up the channel size according to the limit.

If we are outside of these cases, using a different channel size should be done cautiously. Let\u2019s bear in mind that deciding about an accurate queue size isn\u2019t an easy problem:

Martin Thompson

Queues are typically always close to full or close to empty due to the differences in pace between consumers and producers. They very rarely operate in a balanced middle ground where the rate of production and consumption is evenly matched.

"},{"location":"#forgetting-about-possible-side-effects-with-string-formatting-68","title":"Forgetting about possible side effects with string formatting (#68)","text":"TL;DR

Being aware that string formatting may lead to calling existing functions means watching out for possible deadlocks and other data races.

It\u2019s pretty easy to forget the potential side effects of string formatting while working in a concurrent application.

"},{"location":"#etcd-data-race","title":"etcd data race","text":"

github.com/etcd-io/etcd/pull/7816 shows an example of an issue where a map's key was formatted based on a mutable values from a context.

"},{"location":"#deadlock","title":"Deadlock","text":"

Can you see what the problem is in this code with a Customer struct exposing an UpdateAge method and implementing the fmt.Stringer interface?

type Customer struct {\n    mutex sync.RWMutex // Uses a sync.RWMutex to protect concurrent accesses\n    id    string\n    age   int\n}\n\nfunc (c *Customer) UpdateAge(age int) error {\n    c.mutex.Lock() // Locks and defers unlock as we update Customer\n    defer c.mutex.Unlock()\n\n    if age < 0 { // Returns an error if age is negative\n        return fmt.Errorf(\"age should be positive for customer %v\", c)\n    }\n\n    c.age = age\n    return nil\n}\n\nfunc (c *Customer) String() string {\n    c.mutex.RLock() // Locks and defers unlock as we read Customer\n    defer c.mutex.RUnlock()\n    return fmt.Sprintf(\"id %s, age %d\", c.id, c.age)\n}\n

The problem here may not be straightforward. If the provided age is negative, we return an error. Because the error is formatted, using the %s directive on the receiver, it will call the String method to format Customer. But because UpdateAge already acquires the mutex lock, the String method won\u2019t be able to acquire it. Hence, this leads to a deadlock situation. If all goroutines are also asleep, it leads to a panic.

One possible solution is to restrict the scope of the mutex lock:

func (c *Customer) UpdateAge(age int) error {\n    if age < 0 {\n        return fmt.Errorf(\"age should be positive for customer %v\", c)\n    }\n\n    c.mutex.Lock()\n    defer c.mutex.Unlock()\n\n    c.age = age\n    return nil\n}\n

Yet, such an approach isn't always possible. In these conditions, we have to be extremely careful with string formatting.

Another approach is to access the id field directly:

func (c *Customer) UpdateAge(age int) error {\n    c.mutex.Lock()\n    defer c.mutex.Unlock()\n\n    if age < 0 {\n        return fmt.Errorf(\"age should be positive for customer id %s\", c.id)\n    }\n\n    c.age = age\n    return nil\n}\n

In concurrent applications, we should remain cautious about the possible side effects of string formatting.

Source code

"},{"location":"#creating-data-races-with-append-69","title":"Creating data races with append (#69)","text":"TL;DR

Calling append isn\u2019t always data-race-free; hence, it shouldn\u2019t be used concurrently on a shared slice.

Should adding an element to a slice using append is data-race-free? Spoiler: it depends.

Do you believe this example has a data race?

s := make([]int, 1)\n\ngo func() { // In a new goroutine, appends a new element on s\n    s1 := append(s, 1)\n    fmt.Println(s1)\n}()\n\ngo func() { // Same\n    s2 := append(s, 1)\n    fmt.Println(s2)\n}()\n

The answer is no.

In this example, we create a slice with make([]int, 1). The code creates a one-length, one-capacity slice. Thus, because the slice is full, using append in each goroutine returns a slice backed by a new array. It doesn\u2019t mutate the existing array; hence, it doesn\u2019t lead to a data race.

Now, let\u2019s run the same example with a slight change in how we initialize s. Instead of creating a slice with a length of 1, we create it with a length of 0 but a capacity of 1. How about this new example? Does it contain a data race?

s := make([]int, 0, 1)\n\ngo func() { \n    s1 := append(s, 1)\n    fmt.Println(s1)\n}()\n\ngo func() {\n    s2 := append(s, 1)\n    fmt.Println(s2)\n}()\n

The answer is yes. We create a slice with make([]int, 0, 1). Therefore, the array isn\u2019t full. Both goroutines attempt to update the same index of the backing array (index 1), which is a data race.

How can we prevent the data race if we want both goroutines to work on a slice containing the initial elements of s plus an extra element? One solution is to create a copy of s.

We should remember that using append on a shared slice in concurrent applications can lead to a data race. Hence, it should be avoided.

Source code

"},{"location":"#using-mutexes-inaccurately-with-slices-and-maps-70","title":"Using mutexes inaccurately with slices and maps (#70)","text":"TL;DR

Remembering that slices and maps are pointers can prevent common data races.

Let's implement a Cache struct used to handle caching for customer balances. This struct will contain a map of balances per customer ID and a mutex to protect concurrent accesses:

type Cache struct {\n    mu       sync.RWMutex\n    balances map[string]float64\n}\n

Next, we add an AddBalance method that mutates the balances map. The mutation is done in a critical section (within a mutex lock and a mutex unlock):

func (c *Cache) AddBalance(id string, balance float64) {\n    c.mu.Lock()\n    c.balances[id] = balance\n    c.mu.Unlock()\n}\n

Meanwhile, we have to implement a method to calculate the average balance for all the customers. One idea is to handle a minimal critical section this way:

func (c *Cache) AverageBalance() float64 {\n    c.mu.RLock()\n    balances := c.balances // Creates a copy of the balances map\n    c.mu.RUnlock()\n\n    sum := 0.\n    for _, balance := range balances { // Iterates over the copy, outside of the critical section\n        sum += balance\n    }\n    return sum / float64(len(balances))\n}\n

What's the problem with this code?

If we run a test using the -race flag with two concurrent goroutines, one calling AddBalance (hence mutating balances) and another calling AverageBalance, a data race occurs. What\u2019s the problem here?

Internally, a map is a runtime.hmap struct containing mostly metadata (for example, a counter) and a pointer referencing data buckets. So, balances := c.balances doesn\u2019t copy the actual data. Therefore, the two goroutines perform operations on the same data set, and one mutates it. Hence, it's a data race.

One possible solution is to protect the whole AverageBalance function:

func (c *Cache) AverageBalance() float64 {\n    c.mu.RLock()\n    defer c.mu.RUnlock() // Unlocks when the function returns\n\n    sum := 0.\n    for _, balance := range c.balances {\n        sum += balance\n    }\n    return sum / float64(len(c.balances))\n}\n

Another option, if the iteration operation isn\u2019t lightweight, is to work on an actual copy of the data and protect only the copy:

func (c *Cache) AverageBalance() float64 {\n    c.mu.RLock()\n    m := make(map[string]float64, len(c.balances)) // Copies the map\n    for k, v := range c.balances {\n        m[k] = v\n    }\n    c.mu.RUnlock()\n\n    sum := 0.\n    for _, balance := range m {\n        sum += balance\n    }\n    return sum / float64(len(m))\n}\n

Once we have made a deep copy, we release the mutex. The iterations are done on the copy outside of the critical section.

In summary, we have to be careful with the boundaries of a mutex lock. In this section, we have seen why assigning an existing map (or an existing slice) to a map isn\u2019t enough to protect against data races. The new variable, whether a map or a slice, is backed by the same data set. There are two leading solutions to prevent this: protect the whole function, or work on a copy of the actual data. In all cases, let\u2019s be cautious when designing critical sections and make sure the boundaries are accurately defined.

Source code

"},{"location":"#misusing-syncwaitgroup-71","title":"Misusing sync.WaitGroup (#71)","text":"TL;DR

To accurately use sync.WaitGroup, call the Add method before spinning up goroutines.

Source code

"},{"location":"#forgetting-about-synccond-72","title":"Forgetting about sync.Cond (#72)","text":"TL;DR

You can send repeated notifications to multiple goroutines with sync.Cond.

Source code

"},{"location":"#not-using-errgroup-73","title":"Not using errgroup (#73)","text":"TL;DR

You can synchronize a group of goroutines and handle errors and contexts with the errgroup package.

Source code

"},{"location":"#copying-a-sync-type-74","title":"Copying a sync type (#74)","text":"TL;DR

sync types shouldn\u2019t be copied.

Source code

"},{"location":"#standard-library","title":"Standard Library","text":""},{"location":"#providing-a-wrong-time-duration-75","title":"Providing a wrong time duration (#75)","text":"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.

Many common functions in the standard library accept a time.Duration, which is an alias for the int64 type. However, one time.Duration unit represents one nanosecond, instead of one millisecond, as commonly seen in other programming languages. As a result, passing numeric types instead of using the time.Duration API can lead to unexpected behavior.

A developer with experience in other languages might assume that the following code creates a new time.Ticker that delivers ticks every second, given the value 1000:

ticker := time.NewTicker(1000)\nfor {\n    select {\n    case <-ticker.C:\n        // Do something\n    }\n}\n

However, because 1,000 time.Duration units = 1,000 nanoseconds, ticks are delivered every 1,000 nanoseconds = 1 microsecond, not every second as assumed.

We should always use the time.Duration API to avoid confusion and unexpected behavior:

ticker = time.NewTicker(time.Microsecond)\n// Or\nticker = time.NewTicker(1000 * time.Nanosecond)\n

Source code

"},{"location":"#timeafter-and-memory-leaks-76","title":"time.After and memory leaks (#76)","text":"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.

Developers often use time.After in loops or HTTP handlers repeatedly to implement the timing function. But it can lead to unintended peak memory consumption due to the delayed release of resources, just like the following code:

func consumer(ch <-chan Event) {\n    for {\n        select {\n        case event := <-ch:\n            handle(event)\n        case <-time.After(time.Hour):\n            log.Println(\"warning: no messages received\")\n        }\n    }\n}\n

The source code of the function time.After is as follows:

func After(d Duration) <-chan Time {\n    return NewTimer(d).C\n}\n

As we see, it returns receive-only channel.

When time.After is used in a loop or repeated context, a new channel is created in each iteration. If these channels are not properly closed or if their associated timers are not stopped, they can accumulate and consume memory. The resources associated with each timer and channel are only released when the timer expires or the channel is closed.

To avoid this happening, We can use context's timeout setting instead of time.After, like below:

func consumer(ch <-chan Event) {\n    for {\n        ctx, cancel := context.WithTimeout(context.Background(), time.Hour)\n        select {\n        case event := <-ch:\n            cancel()\n            handle(event)\n        case <-ctx.Done():\n            log.Println(\"warning: no messages received\")\n        }\n    }\n}\n

We can also use time.NewTimer like so:

func consumer(ch <-chan Event) {\n    timerDuration := 1 * time.Hour\n    timer := time.NewTimer(timerDuration)\n\n    for {\n        timer.Reset(timerDuration)\n        select {\n        case event := <-ch:\n            handle(event)\n        case <-timer.C:\n            log.Println(\"warning: no messages received\")\n        }\n    }\n}\n

Source code

"},{"location":"#json-handling-common-mistakes-77","title":"JSON handling common mistakes (#77)","text":"
  • Unexpected behavior because of type embedding

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

  • 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

  • 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

"},{"location":"#common-sql-mistakes-78","title":"Common SQL mistakes (#78)","text":"
  • Forgetting that sql.Open doesn't necessarily establish connections to a database

Call the Ping or PingContext method if you need to test your configuration and make sure a database is reachable.

Source code

  • Forgetting about connections pooling

Configure the database connection parameters for production-grade applications.

  • Not using prepared statements

Using SQL prepared statements makes queries more efficient and more secure.

Source code

  • Mishandling null values

Deal with nullable columns in tables using pointers or sql.NullXXX types.

Source code

  • Not handling rows iteration errors

Call the Err method of sql.Rows after row iterations to ensure that you haven\u2019t missed an error while preparing the next row.

Source code

"},{"location":"#not-closing-transient-resources-http-body-sqlrows-and-osfile-79","title":"Not closing transient resources (HTTP body, sql.Rows, and os.File) (#79)","text":"TL;DR

Eventually close all structs implementing io.Closer to avoid possible leaks.

Source code

"},{"location":"#forgetting-the-return-statement-after-replying-to-an-http-request-80","title":"Forgetting the return statement after replying to an HTTP request (#80)","text":"TL;DR

To avoid unexpected behaviors in HTTP handler implementations, make sure you don\u2019t miss the return statement if you want a handler to stop after http.Error.

Consider the following HTTP handler that handles an error from foo using http.Error:

func handler(w http.ResponseWriter, req *http.Request) {\n    err := foo(req)\n    if err != nil {\n        http.Error(w, \"foo\", http.StatusInternalServerError)\n    }\n\n    _, _ = w.Write([]byte(\"all good\"))\n    w.WriteHeader(http.StatusCreated)\n}\n

If we run this code and err != nil, the HTTP response would be:

foo\nall good\n

The response contains both the error and success messages, and also the first HTTP status code, 500. There would also be a warning log indicating that we attempted to write the status code multiple times:

2023/10/10 16:45:33 http: superfluous response.WriteHeader call from main.handler (main.go:20)\n

The mistake in this code is that http.Error does not stop the handler's execution, which means the success message and status code get written in addition to the error. Beyond an incorrect response, failing to return after writing an error can lead to the unwanted execution of code and unexpected side-effects. The following code adds the return statement following the http.Error and exhibits the desired behavior when ran:

func handler(w http.ResponseWriter, req *http.Request) {\n    err := foo(req)\n    if err != nil {\n        http.Error(w, \"foo\", http.StatusInternalServerError)\n        return // Adds the return statement\n    }\n\n    _, _ = w.Write([]byte(\"all good\"))\n    w.WriteHeader(http.StatusCreated)\n}\n

Source code

"},{"location":"#using-the-default-http-client-and-server-81","title":"Using the default HTTP client and server (#81)","text":"TL;DR

For production-grade applications, don\u2019t use the default HTTP client and server implementations. These implementations are missing timeouts and behaviors that should be mandatory in production.

Source code

"},{"location":"#testing","title":"Testing","text":""},{"location":"#not-categorizing-tests-build-tags-environment-variables-and-short-mode-82","title":"Not categorizing tests (build tags, environment variables, and short mode) (#82)","text":"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

"},{"location":"#not-enabling-the-race-flag-83","title":"Not enabling the race flag (#83)","text":"TL;DR

Enabling the -race flag is highly recommended when writing concurrent applications. Doing so allows you to catch potential data races that can lead to software bugs.

In Go, the race detector isn\u2019t a static analysis tool used during compilation; instead, it\u2019s a tool to find data races that occur at runtime. To enable it, we have to enable the -race flag while compiling or running a test. For example:

go test -race ./...\n

Once the race detector is enabled, the compiler instruments the code to detect data races. Instrumentation refers to a compiler adding extra instructions: here, tracking all memory accesses and recording when and how they occur.

Enabling the race detector adds an overhead in terms of memory and execution time; hence, it's generally recommended to enable it only during local testing or continuous integration, not production.

If a race is detected, Go raises a warning. For example:

package main\n\nimport (\n    \"fmt\"\n)\n\nfunc main() {\n    i := 0\n    go func() { i++ }()\n    fmt.Println(i)\n}\n

Runnig this code with the -race logs the following warning:

==================\nWARNING: DATA RACE\nWrite at 0x00c000026078 by goroutine 7: # (1)\n  main.main.func1()\n      /tmp/app/main.go:9 +0x4e\n\nPrevious read at 0x00c000026078 by main goroutine: # (2)\n  main.main()\n      /tmp/app/main.go:10 +0x88\n\nGoroutine 7 (running) created at: # (3)\n  main.main()\n      /tmp/app/main.go:9 +0x7a\n==================\n
  1. Indicates that goroutine 7 was writing
  2. Indicates that the main goroutine was reading
  3. Indicates when the goroutine 7 was created

Let\u2019s make sure we are comfortable reading these messages. Go always logs the following:

  • The concurrent goroutines that are incriminated: here, the main goroutine and goroutine 7.
  • Where accesses occur in the code: in this case, lines 9 and 10.
  • When these goroutines were created: goroutine 7 was created in main().

In addition, if a specific file contains tests that lead to data races, we can exclude it from race detection using the !race build tag:

//go:build !race\n\npackage main\n\nimport (\n    \"testing\"\n)\n\nfunc TestFoo(t *testing.T) {\n    // ...\n}\n
"},{"location":"#not-using-test-execution-modes-parallel-and-shuffle-84","title":"Not using test execution modes (parallel and shuffle) (#84)","text":"TL;DR

Using the -parallel flag is an efficient way to speed up tests, especially long-running ones. Use the -shuffle flag to help ensure that a test suite doesn\u2019t rely on wrong assumptions that could hide bugs.

"},{"location":"#not-using-table-driven-tests-85","title":"Not using table-driven tests (#85)","text":"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

"},{"location":"#sleeping-in-unit-tests-86","title":"Sleeping in unit tests (#86)","text":"TL;DR

Avoid sleeps using synchronization to make a test less flaky and more robust. If synchronization isn\u2019t possible, consider a retry approach.

Source code

"},{"location":"#not-dealing-with-the-time-api-efficiently-87","title":"Not dealing with the time API efficiently (#87)","text":"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

"},{"location":"#not-using-testing-utility-packages-httptest-and-iotest-88","title":"Not using testing utility packages (httptest and iotest) (#88)","text":"
  • The httptest package is helpful for dealing with HTTP applications. It provides a set of utilities to test both clients and servers.

Source code

  • The iotest package helps write io.Reader and test that an application is tolerant to errors.

Source code

"},{"location":"#writing-inaccurate-benchmarks-89","title":"Writing inaccurate benchmarks (#89)","text":"TL;DR

Regarding benchmarks:

  • Use time methods to preserve the accuracy of a benchmark.
  • Increasing benchtime or using tools such as benchstat can be helpful when dealing with micro-benchmarks.
  • 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.
  • Make sure the function under test leads to a side effect, to prevent compiler optimizations from fooling you about the benchmark results.
  • To prevent the observer effect, force a benchmark to re-create the data used by a CPU-bound function.

Read the full section here.

Source code

"},{"location":"#not-exploring-all-the-go-testing-features-90","title":"Not exploring all the Go testing features (#90)","text":"
  • Code coverage

Use code coverage with the -coverprofile flag to quickly see which part of the code needs more attention.

  • Testing from a different package

Place unit tests in a different package to enforce writing tests that focus on an exposed behavior, not internals.

Source code

  • 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

  • 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

"},{"location":"#not-using-fuzzing-community-mistake","title":"Not using fuzzing (community mistake)","text":"TL;DR

Fuzzing is an efficient strategy to detect random, unexpected, or malformed inputs to complex functions and methods in order to discover vulnerabilities, bugs, or even potential crashes.

Credits: @jeromedoucet

"},{"location":"#optimizations","title":"Optimizations","text":""},{"location":"#not-understanding-cpu-caches-91","title":"Not understanding CPU caches (#91)","text":"
  • CPU architecture

Understanding how to use CPU caches is important for optimizing CPU-bound applications because the L1 cache is about 50 to 100 times faster than the main memory.

  • Cache line

Being conscious of the cache line concept is critical to understanding how to organize data in data-intensive applications. A CPU doesn\u2019t 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

  • Slice of structs vs. struct of slices

Source code

  • 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\u2019t predictable.

Source code

  • Cache placement policy

To avoid a critical stride, hence utilizing only a tiny portion of the cache, be aware that caches are partitioned.

"},{"location":"#writing-concurrent-code-that-leads-to-false-sharing-92","title":"Writing concurrent code that leads to false sharing (#92)","text":"TL;DR

Knowing that lower levels of CPU caches aren\u2019t shared across all the cores helps avoid performance-degrading patterns such as false sharing while writing concurrency code. Sharing memory is an illusion.

Read the full section here.

Source code

"},{"location":"#not-taking-into-account-instruction-level-parallelism-93","title":"Not taking into account instruction-level parallelism (#93)","text":"TL;DR

Use 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

"},{"location":"#not-being-aware-of-data-alignment-94","title":"Not being aware of data alignment (#94)","text":"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

"},{"location":"#not-understanding-stack-vs-heap-95","title":"Not understanding stack vs. heap (#95)","text":"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

"},{"location":"#not-knowing-how-to-reduce-allocations-api-change-compiler-optimizations-and-syncpool-96","title":"Not knowing how to reduce allocations (API change, compiler optimizations, and sync.Pool) (#96)","text":"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

"},{"location":"#not-relying-on-inlining-97","title":"Not relying on inlining (#97)","text":"TL;DR

Use the fast-path inlining technique to efficiently reduce the amortized time to call a function.

"},{"location":"#not-using-go-diagnostics-tooling-98","title":"Not using Go diagnostics tooling (#98)","text":"TL;DR

Rely on profiling and the execution tracer to understand how an application performs and the parts to optimize.

Read the full section here.

"},{"location":"#not-understanding-how-the-gc-works-99","title":"Not understanding how the GC works (#99)","text":"TL;DR

Understanding how to tune the GC can lead to multiple benefits such as handling sudden load increases more efficiently.

"},{"location":"#not-understanding-the-impacts-of-running-go-in-docker-and-kubernetes-100","title":"Not understanding the impacts of running Go in Docker and Kubernetes (#100)","text":"TL;DR

To help avoid CPU throttling when deployed in Docker and Kubernetes, keep in mind that Go isn\u2019t CFS-aware.

By default, GOMAXPROCS is set to the number of OS-apparent logical CPU cores.

When running some Go code inside Docker and Kubernetes, we must know that Go isn't CFS-aware (github.com/golang/go/issues/33803). Therefore, GOMAXPROCS isn't automatically set to the value of spec.containers.resources.limits.cpu (see Kubernetes Resource Management for Pods and Containers); instead, it's set to the number of logical cores on the host machine. The main implication is that it can lead to an increased tail latency in some specific situations.

One solution is to rely on uber-go/automaxprocs that automatically set GOMAXPROCS to match the Linux container CPU quota.

"},{"location":"#community","title":"Community","text":"

Thanks to all the contributors:

"},{"location":"20-slice/","title":"Not understanding slice length and capacity","text":"

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

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

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

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

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

Figure 1: A three-length, six-capacity slice.

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

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

Figure 2: Updating the slice\u2019s second element: s[1] = 1.

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

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

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

s = append(s, 2)\n

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

Figure 3: Appending an element to s.

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

s = append(s, 3, 4, 5)\nfmt.Println(s)\n

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

[0 1 0 2 3 4 5]\n

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

Figure 4: Because the initial backing array is full, Go creates another array and copies all the elements.

The slice now references the new backing array. What will happen to the previous backing array? If it\u2019s no longer referenced, it\u2019s eventually freed by the garbage collector (GC) if allocated on the heap. (We discuss heap memory in mistake #95, \u201cNot understanding stack vs. heap,\u201d and we look at how the GC works in mistake #99, \u201cNot understanding how the GC works.\u201d)

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

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

Figure 5: The slices s1 and s2 reference the same backing array with different lengths and capacities.

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

Figure 6: Because s1 and s2 are backed by the same array, updating a common element makes the change visible in both slices.

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

s2 = append(s2, 2)\n

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

Figure 7: Appending an element to s2.

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

s1=[0 1 0], s2=[1 0 2]\n

It\u2019s important to understand this behavior so that we don\u2019t make wrong assumptions while using append.

Note

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

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

s2 = append(s2, 3)\ns2 = append(s2, 4) // At this stage, the backing is already full\ns2 = append(s2, 5)\n

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

Figure 8: Appending elements to s2 until the backing array is full.

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

To summarize, the slice length is the number of available elements in the slice, whereas the slice capacity is the number of elements in the backing array. Adding an element to a full slice (length == capacity) leads to creating a new backing array with a new capacity, copying all the elements from the previous array, and updating the slice pointer to the new array.

"},{"location":"28-maps-memory-leaks/","title":"Maps and memory leaks","text":"

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

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

m := make(map[int][128]byte)\n

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

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

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

func main() {\n    n := 1_000_000\n    m := make(map[int][128]byte)\n    printAlloc()\n\n    for i := 0; i < n; i++ { // Adds 1 million elements\n        m[i] = [128]byte{}\n    }\n    printAlloc()\n\n    for i := 0; i < n; i++ { // Deletes 1 million elements\n        delete(m, i)\n    }\n\n    runtime.GC() // Triggers a manual GC\n    printAlloc()\n    runtime.KeepAlive(m) // Keeps a reference to m so that the map isn\u2019t collected\n}\n\nfunc printAlloc() {\n    var m runtime.MemStats\n    runtime.ReadMemStats(&m)\n    fmt.Printf(\"%d MB\\n\", m.Alloc/(1024*1024))\n}\n

We allocate an empty map, add 1 million elements, remove 1 million elements, and then run a GC. We also make sure to keep a reference to the map using runtime.KeepAlive so that the map isn\u2019t collected as well. Let\u2019s run this example:

0 MB   <-- After m is allocated\n461 MB <-- After we add 1 million elements\n293 MB <-- After we remove 1 million elements\n

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

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

Figure 1: A hash table example with a focus on bucket 0.

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

Figure 2: In case of a bucket overflow, Go allocates a new bucket and links the previous bucket to it.

Under the hood, a Go map is a pointer to a runtime.hmap struct. This struct contains multiple fields, including a B field, giving the number of buckets in the map:

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

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

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

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

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

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

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

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

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

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

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

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

"},{"location":"5-interface-pollution/","title":"Interface pollution","text":"

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

"},{"location":"5-interface-pollution/#concepts","title":"Concepts","text":"

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

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

The io.Reader contains a single Read method:

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

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

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

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

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

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

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

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

func copySourceToDest(source io.Reader, dest io.Writer) error {\n    // ...\n}\n

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

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

func TestCopySourceToDest(t *testing.T) {\n    const input = \"foo\"\n    source := strings.NewReader(input) // Creates an io.Reader\n    dest := bytes.NewBuffer(make([]byte, 0)) // Creates an io.Writer\n\n    err := copySourceToDest(source, dest) // Calls copySourceToDest from a *strings.Reader and a *bytes.Buffer\n    if err != nil {\n        t.FailNow()\n    }\n\n    got := dest.String()\n    if got != input {\n        t.Errorf(\"expected: %s, got: %s\", input, got)\n    }\n}\n

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

While designing interfaces, the granularity (how many methods the interface contains) is also something to keep in mind. A known proverb in Go relates to how big an interface should be:

Rob Pike

The bigger the interface, the weaker the abstraction.

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

type ReadWriter interface {\n    Reader\n    Writer\n}\n
Note

As Einstein said, \u201cEverything should be made as simple as possible, but no simpler.\u201d Applied to interfaces, this denotes that finding the perfect granularity for an interface isn\u2019t necessarily a straightforward process.

Let\u2019s now discuss common cases where interfaces are recommended.

"},{"location":"5-interface-pollution/#when-to-use-interfaces","title":"When to use interfaces","text":"

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

  • Common behavior
  • Decoupling
  • Restricting behavior
"},{"location":"5-interface-pollution/#common-behavior","title":"Common behavior","text":"

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

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

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

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

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

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

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

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

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

Let\u2019s now see another main use case when using interfaces.

"},{"location":"5-interface-pollution/#decoupling","title":"Decoupling","text":"

Another important use case is about decoupling our code from an implementation. If we rely on an abstraction instead of a concrete implementation, the implementation itself can be replaced with another without even having to change our code. This is the Liskov Substitution Principle (the L in Robert C. Martin\u2019s SOLID design principles).

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

type CustomerService struct {\n    store mysql.Store // Depends on the concrete implementation\n}\n\nfunc (cs CustomerService) CreateNewCustomer(id string) error {\n    customer := Customer{id: id}\n    return cs.store.StoreCustomer(customer)\n}\n

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

type customerStorer interface { // Creates a storage abstraction\n    StoreCustomer(Customer) error\n}\n\ntype CustomerService struct {\n    storer customerStorer // Decouples CustomerService from the actual implementation\n}\n\nfunc (cs CustomerService) CreateNewCustomer(id string) error {\n    customer := Customer{id: id}\n    return cs.storer.StoreCustomer(customer)\n}\n

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

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

Let\u2019s now discuss another use case: to restrict a behavior.

"},{"location":"5-interface-pollution/#restricting-behavior","title":"Restricting behavior","text":"

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

type IntConfig struct {\n    // ...\n}\n\nfunc (c *IntConfig) Get() int {\n    // Retrieve configuration\n}\n\nfunc (c *IntConfig) Set(value int) {\n    // Update configuration\n}\n

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

type intConfigGetter interface {\n    Get() int\n}\n

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

type Foo struct {\n    threshold intConfigGetter\n}\n\nfunc NewFoo(threshold intConfigGetter) Foo { // Injects the configuration getter\n    return Foo{threshold: threshold}\n}\n\nfunc (f Foo) Bar()  {\n    threshold := f.threshold.Get() // Reads the configuration\n    // ...\n}\n

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

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

Now, let\u2019s finish this section and discuss the problems with interface pollution.

"},{"location":"5-interface-pollution/#interface-pollution_1","title":"Interface pollution","text":"

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

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

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

Note

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

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

Rob Pike

Don\u2019t design with interfaces, discover them.

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

"},{"location":"56-concurrency-faster/","title":"Thinking concurrency is always faster","text":"

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

"},{"location":"56-concurrency-faster/#go-scheduling","title":"Go Scheduling","text":"

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

  • Concurrent \u2014 Two or more threads can start, run, and complete in overlapping time periods.
  • Parallel \u2014 The same task can be executed multiple times at once.

The OS is responsible for scheduling the thread\u2019s processes optimally so that:

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

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

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

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

Note

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

Let\u2019s now discuss how the Go scheduler works to overview how goroutines are handled. Internally, the Go scheduler uses the following terminology (see proc.go):

  • G \u2014 Goroutine
  • M \u2014 OS thread (stands for machine)
  • P \u2014 CPU core (stands for processor)

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

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

  • Executing \u2014 The goroutine is scheduled on an M and executing its instructions.
  • Runnable \u2014 The goroutine is waiting to be in an executing state.
  • Waiting \u2014 The goroutine is stopped and pending something completing, such as a system call or a synchronization operation (such as acquiring a mutex).

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

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

Figure 1: An example of the current state of a Go application executed on a four-core machine. Goroutines that aren\u2019t in an executing state are either runnable (pending being executed) or waiting (pending a blocking operation)

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

P0, P1, and P3 are currently busy executing Go runtime threads. But P2 is presently idle as M3 is switched off P2, and there\u2019s no goroutine to be executed. This isn\u2019t a good situation because six runnable goroutines are pending being executed, some in the global queue and some in other local queues. How will the Go runtime handle this situation? Here\u2019s the scheduling implementation in pseudocode (see proc.go):

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

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

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

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

"},{"location":"56-concurrency-faster/#parallel-merge-sort","title":"Parallel Merge Sort","text":"

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

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

Figure 2: Applying the merge sort algorithm repeatedly breaks each list into two sublists. Then the algorithm uses a merge operation such that the resulting list is sorted

Here is the sequential implementation of this algorithm. We don\u2019t include all of the code as it\u2019s not the main point of this section:

func sequentialMergesort(s []int) {\n    if len(s) <= 1 {\n        return\n    }\n\n    middle := len(s) / 2\n    sequentialMergesort(s[:middle]) // First half\n    sequentialMergesort(s[middle:]) // Second half\n    merge(s, middle) // Merges the two halves\n}\n\nfunc merge(s []int, middle int) {\n    // ...\n}\n

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

func parallelMergesortV1(s []int) {\n    if len(s) <= 1 {\n        return\n    }\n\n    middle := len(s) / 2\n\n    var wg sync.WaitGroup\n    wg.Add(2)\n\n    go func() { // Spins up the first half of the work in a goroutine\n        defer wg.Done()\n        parallelMergesortV1(s[:middle])\n    }()\n\n    go func() { // Spins up the second half of the work in a goroutine\n        defer wg.Done()\n        parallelMergesortV1(s[middle:])\n    }()\n\n    wg.Wait()\n    merge(s, middle) // Merges the halves\n}\n

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

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

Benchmark_sequentialMergesort-4       2278993555 ns/op\nBenchmark_parallelMergesortV1-4      17525998709 ns/op\n

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

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

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

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

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

const max = 2048 // Defines the threshold\n\nfunc parallelMergesortV2(s []int) {\n    if len(s) <= 1 {\n        return\n    }\n\n    if len(s) <= max {\n        sequentialMergesort(s) // Calls our initial sequential version\n    } else { // If bigger than the threshold, keeps the parallel version\n        middle := len(s) / 2\n\n        var wg sync.WaitGroup\n        wg.Add(2)\n\n        go func() {\n            defer wg.Done()\n            parallelMergesortV2(s[:middle])\n        }()\n\n        go func() {\n            defer wg.Done()\n            parallelMergesortV2(s[middle:])\n        }()\n\n        wg.Wait()\n        merge(s, middle)\n    }\n}\n

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

Benchmark_sequentialMergesort-4       2278993555 ns/op\nBenchmark_parallelMergesortV1-4      17525998709 ns/op\nBenchmark_parallelMergesortV2-4       1313010260 ns/op\n

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

Note

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

"},{"location":"56-concurrency-faster/#conclusion","title":"Conclusion","text":"

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

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

Does this mean we shouldn\u2019t use concurrency? Of course not. However, it\u2019s essential to keep these conclusions in mind. If we\u2019re not sure that a parallel version will be faster, the right approach may be to start with a simple sequential version and build from there using profiling (mistake #98, \u201cNot using Go diagnostics tooling\u201d) and benchmarks (mistake #89, \u201cWriting inaccurate benchmarks\u201d), for example. It can be the only way to ensure that a concurrent implementation is worth it.

"},{"location":"89-benchmarks/","title":"Writing inaccurate benchmarks","text":"

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

  • Not resetting or pausing the timer
  • Making wrong assumptions about micro-benchmarks
  • Not being careful about compiler optimizations
  • Being fooled by the observer effect
"},{"location":"89-benchmarks/#general-concepts","title":"General concepts","text":"

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

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

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

$ go test -bench=.\ncpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz\nBenchmarkFoo-4                73          16511228 ns/op\n

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

$ go test -bench=. -benchtime=2s\nBenchmarkFoo-4               150          15832169 ns/op\n

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

Next, let\u2019s look at some common traps.

"},{"location":"89-benchmarks/#not-resetting-or-pausing-the-timer","title":"Not resetting or pausing the timer","text":"

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

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

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

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

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

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

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

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

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

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

Note

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

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

"},{"location":"89-benchmarks/#making-wrong-assumptions-about-micro-benchmarks","title":"Making wrong assumptions about micro-benchmarks","text":"

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

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

If we run this benchmark, here\u2019s some example output:

cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz\nBenchmarkAtomicStoreInt32\nBenchmarkAtomicStoreInt32-4    197107742           5.682 ns/op\nBenchmarkAtomicStoreInt64\nBenchmarkAtomicStoreInt64-4    213917528           5.134 ns/op\n

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

BenchmarkAtomicStoreInt64\nBenchmarkAtomicStoreInt64-4    224900722           5.434 ns/op\nBenchmarkAtomicStoreInt32\nBenchmarkAtomicStoreInt32-4    230253900           5.159 ns/op\n

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

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

Note

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

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

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

Let\u2019s run the benchmark 10 times using the -count option and pipe the output to a specific file:

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

We can then run benchstat on this file:

$ benchstat stats.txt\nname                time/op\nAtomicStoreInt32-4  5.10ns \u00b1 1%\nAtomicStoreInt64-4  5.10ns \u00b1 1%\n

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

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

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

"},{"location":"89-benchmarks/#not-being-careful-about-compiler-optimizations","title":"Not being careful about compiler optimizations","text":"

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

const m1 = 0x5555555555555555\nconst m2 = 0x3333333333333333\nconst m4 = 0x0f0f0f0f0f0f0f0f\nconst h01 = 0x0101010101010101\n\nfunc popcnt(x uint64) uint64 {\n    x -= (x >> 1) & m1\n    x = (x & m2) + ((x >> 2) & m2)\n    x = (x + (x >> 4)) & m4\n    return (x * h01) >> 56\n}\n

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

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

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

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

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

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

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

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

In our case, we write the following benchmark:

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

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

Note

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

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

cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz\nBenchmarkPopcnt1-4      1000000000               0.2858 ns/op\nBenchmarkPopcnt2-4      606402058                1.993 ns/op\n

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

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

"},{"location":"89-benchmarks/#being-fooled-by-the-observer-effect","title":"Being fooled by the observer effect","text":"

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

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

Figure 1: Computing the sum of the first eight columns.

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

func calculateSum512(s [][512]int64) int64 {\n    var sum int64\n    for i := 0; i < len(s); i++ { // Iterate over each row\n        for j := 0; j < 8; j++ { // Iterate over the first eight columns\n            sum += s[i][j] // Increment sum\n        }\n    }\n    return sum\n}\n\nfunc calculateSum513(s [][513]int64) int64 {\n    // Same implementation as calculateSum512\n}\n

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

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

const rows = 1000\n\nvar res int64\n\nfunc BenchmarkCalculateSum512(b *testing.B) {\n    var sum int64\n    s := createMatrix512(rows) // Create a matrix of 512 columns\n    b.ResetTimer()\n    for i := 0; i < b.N; i++ {\n        sum = calculateSum512(s) // Create a matrix of 512 columns\n    }\n    res = sum\n}\n\nfunc BenchmarkCalculateSum513(b *testing.B) {\n    var sum int64\n    s := createMatrix513(rows) // Create a matrix of 513 columns\n    b.ResetTimer()\n    for i := 0; i < b.N; i++ {\n        sum = calculateSum513(s) // Calculate the sum\n    }\n    res = sum\n}\n

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

cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz\nBenchmarkCalculateSum512-4        81854             15073 ns/op\nBenchmarkCalculateSum513-4       161479              7358 ns/op\n

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

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

Note

This isn\u2019t in the scope of this post to explain why, but we look at this problem in 100 Go Mistakes, mistake #91: \u201cNot understanding CPU caches.\u201d

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

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

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

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

cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz\nBenchmarkCalculateSum512-4         1116             33547 ns/op\nBenchmarkCalculateSum513-4          998             35507 ns/op\n

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

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

"},{"location":"9-generics/","title":"Being confused about when to use generics","text":"

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.

"},{"location":"9-generics/#concepts","title":"Concepts","text":"

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

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

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

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

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

We can start noticing a couple of issues:

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

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

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

func foo[T any](t T) {\n    // ...\n}\n

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

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

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

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

var m map[[]byte]int\n

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

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

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

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

type customConstraint interface {\n   ~int | ~string // Define a custom type that will restrict types to int and string\n}\n\n// Change the type parameter K to be custom\nfunc getKeys[K customConstraint, V any](m map[K]V) []K {\n   // Same implementation\n}\n

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

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

m = map[string]int{\n   \"one\":   1,\n   \"two\":   2,\n   \"three\": 3,\n}\nkeys := getKeys(m)\n

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

keys := getKeys[string](m)\n
Note

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

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

type customConstraint interface {\n   ~int\n   String() string\n}\n

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

type customInt int\n\nfunc (i customInt) String() string {\n   return strconv.Itoa(int(i))\n}\n

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

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

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

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

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

type Node[T any] struct { // Use type parameter\n   Val  T\n   next *Node[T]\n}\n\nfunc (n *Node[T]) Add(next *Node[T]) { // Instantiate type receiver\n   n.next = next\n}\n

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

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

type Foo struct {}\n\nfunc (Foo) bar[T any](t T) {}\n
./main.go:29:15: methods cannot have type parameters\n

Now, let\u2019s delve into concrete cases where we should and shouldn\u2019t use generics.

"},{"location":"9-generics/#common-uses-and-misuses","title":"Common uses and misuses","text":"

So when are generics useful? Let\u2019s discuss a couple of common uses where generics are recommended:

  • Data structures. For example, we can use generics to factor out the element type if we implement a binary tree, a linked list, or a heap.
  • Functions working with slices, maps, and channels of any type. For example, a function to merge two channels would work with any channel type. Hence, we could use type parameters to factor out the channel type:
func merge[T any](ch1, ch2 <-chan T) <-chan T {\n    // ...\n}\n
  • Meanwhile, instead of factoring out a type, we can factor out behaviors. For example, the sort package contains functions to sort different slice types such as sort.Ints or sort.Float64s. Using type parameters, we can factor out the sorting behaviors that rely on three methods, Len, Less, and Swap:
type sliceFn[T any] struct { // Use type parameter\n   s       []T\n   compare func(T, T) bool // Compare two T elements\n}\n\nfunc (s sliceFn[T]) Len() int           { return len(s.s) }\nfunc (s sliceFn[T]) Less(i, j int) bool { return s.compare(s.s[i], s.s[j]) }\nfunc (s sliceFn[T]) Swap(i, j int)      { s.s[i], s.s[j] = s.s[j], s.s[i] }\n

Conversely, when is it recommended not to use generics?

  • When just calling a method of the type argument. For example, consider a function that receives an io.Writer and call the Write method:
func foo[T io.Writer](w T) {\n   b := getBytes()\n   _, _ = w.Write(b)\n}\n
  • 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\u2019t make our code clearer, we should probably reconsider our decision for this particular use case.
"},{"location":"9-generics/#conclusion","title":"Conclusion","text":"

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

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

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

"},{"location":"92-false-sharing/","title":"Writing concurrent code that leads to false sharing","text":"

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

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

type Input struct {\n    a int64\n    b int64\n}\n\ntype Result struct {\n    sumA int64\n    sumB int64\n}\n

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

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

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

func count(inputs []Input) Result {\n    wg := sync.WaitGroup{}\n    wg.Add(2)\n\n    result := Result{} // Init the result struct\n\n    go func() {\n        for i := 0; i < len(inputs); i++ {\n            result.sumA += inputs[i].a // Computes sumA\n        }\n        wg.Done()\n    }()\n\n    go func() {\n        for i := 0; i < len(inputs); i++ {\n            result.sumB += inputs[i].b // Computes sumB\n        }\n        wg.Done()\n    }()\n\n    wg.Wait()\n    return result\n}\n

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

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

In this example, sumA and sumB are part of the same memory block.

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

Each block is copied to a cache line on both code 0 and core 1.

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

Each goroutine updates its own variable.

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

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

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

Note

Internally, a CPU uses the MESI protocol to guarantee cache coherency. It tracks each cache line, marking it modified, exclusive, shared, or invalid (MESI).

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

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

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

type Result struct {\n    sumA int64\n    _    [56]byte // Padding\n    sumB int64\n}\n

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

sumA and sumB are part of different memory blocks.

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

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

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

"},{"location":"98-profiling-execution-tracing/","title":"Not using Go diagnostics tooling","text":"

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

"},{"location":"98-profiling-execution-tracing/#profiling","title":"Profiling","text":"

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

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

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

"},{"location":"98-profiling-execution-tracing/#enabling-pprof","title":"Enabling pprof","text":"

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

package main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"net/http\"\n    _ \"net/http/pprof\" // Blank import to pprof\n)\n\nfunc main() {\n    // Exposes an HTTP endpoint\n    http.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n        fmt.Fprintf(w, \"\")\n    })\n    log.Fatal(http.ListenAndServe(\":80\", nil))\n}\n

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

Now that we have seen how to expose a pprof endpoint, let\u2019s discuss the most common profiles.

"},{"location":"98-profiling-execution-tracing/#cpu-profiling","title":"CPU Profiling","text":"

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

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

Note

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

$ go test -bench=. -cpuprofile profile.out\n

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

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

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

Figure 1: The call graph of an application during 30 seconds.

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

Figure 2: Example call graph.

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

Note

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

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

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

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

Note

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

"},{"location":"98-profiling-execution-tracing/#heap-profiling","title":"Heap Profiling","text":"

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

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

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

Figure 3: A heap graph.

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

  • alloc_objects\u2014 Total number of objects allocated
  • alloc_space\u2014 Total amount of memory allocated
  • inuse_objects \u2014 Number of objects allocated and not yet released
  • inuse_space\u2014 Amount of memory allocated and not yet released

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

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

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

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

  1. Go to /debug/pprof/heap?gc=1 (trigger the GC and download the heap profile).
  2. Wait for a few seconds/minutes.
  3. Go to /debug/pprof/heap?gc=1 again.
  4. Use go tool to compare both heap profiles:
$ go tool pprof -http=:8080 -diff_base <file2> <file1>\n

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

Figure 4: The differences between the two heap profiles. Note

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

"},{"location":"98-profiling-execution-tracing/#goroutine-profiling","title":"Goroutine Profiling","text":"

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

Figure 5: Goroutine graph.

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

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

"},{"location":"98-profiling-execution-tracing/#block-profiling","title":"Block Profiling","text":"

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

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

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

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

Note

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

goroutine 2494290 [chan receive, 1420 minutes]:\ngithub.com/Shopify/sarama.(*syncProducer).SendMessages(0xc00071a090,\n[CA]{0xc0009bb800, 0xfb, 0xfb})\n/app/vendor/github.com/Shopify/sarama/sync_producer.go:117 +0x149\n
"},{"location":"98-profiling-execution-tracing/#mutex-profiling","title":"Mutex Profiling","text":"

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

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

Following are a few additional notes about profiling:

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

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

Let\u2019s now look at the execution tracer.

"},{"location":"98-profiling-execution-tracing/#execution-tracer","title":"Execution Tracer","text":"

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

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

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

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

$ go test -bench=. -v -trace=trace.out\n
Note

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

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

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

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

Figure 6: Showing goroutine activity and runtime events such as a GC phase.

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

Figure 7: Too many small bars mean poorly parallelized execution.

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

Figure 8: About 50% of CPU time is spent handling goroutine switches.

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

Figure 9: The number of white spaces has been significantly reduced, proving that the CPU is more fully occupied.

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

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

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

var v int64\n// Creates a fibonacci task\nctx, fibTask := trace.NewTask(context.Background(), \"fibonacci\")\ntrace.WithRegion(ctx, \"main\", func() {\n    v = fibonacci(10)\n})\nfibTask.End()\n\n// Creates a store task\nctx, fibStore := trace.NewTask(ctx, \"store\")\ntrace.WithRegion(ctx, \"main\", func() {\n    atomic.StoreInt64(&result, v)\n})\nfibStore.End()\n

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

Figure 10: Distribution of user-level tasks.

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

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

  • CPU profiling:
    • Sample-based
    • Per function
    • Doesn\u2019t go below the sampling rate (10 ms by default)
  • User-level traces:
    • Not sample-based
    • Per-goroutine execution (unless we use the runtime/trace package)
    • Time executions aren\u2019t bound by any rate

In summary, the execution tracer is a powerful tool for understanding how an application performs. As we have seen with the merge sort example, we can identify poorly parallelized execution. However, the tracer\u2019s granularity remains per goroutine unless we manually use runtime/trace compared to a CPU profile, for example. We can use both profiling and the execution tracer to get the most out of the standard Go diagnostics tools when optimizing an application.

"},{"location":"book/","title":"100 Go Mistakes and How to Avoid Them","text":""},{"location":"book/#description","title":"Description","text":"

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

Read a summary of the 100 mistakes or the first chapter.

"},{"location":"book/#quotes-and-ratings","title":"Quotes and Ratings","text":"

Krystian (Goodreads user)

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

Akash Chetty

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

Neeraj Shah

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

Anupam Sengupta

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

Manning, Goodreads, and Amazon reviews: 4.7/5 avg rating"},{"location":"book/#where-to-buy","title":"Where to Buy?","text":"
  • 100 Go Mistakes and How to Avoid Them (\ud83c\uddec\ud83c\udde7 edition: paper, digital, or audiobook)

    • Manning (please make sure to use my personal discount code for -35%: au35har)
    • O\u2019Reilly
    • Amazon: .com, .co.uk, .de, .fr, .in, .co.jp, .es, .it, .com.br
  • Go\u8a00\u8a9e100Tips \u958b\u767a\u8005\u306b\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044\u3078\u306e\u5bfe\u51e6\u6cd5 (\ud83c\uddef\ud83c\uddf5 edition: paper or digital)

    • Amazon: .co.jp
  • 100\u4e2aGo\u8bed\u8a00\u5178\u578b\u9519\u8bef (\ud83c\udde8\ud83c\uddf3 edition: paper or digital)

    • Douban.com
  • Go 100\uac00\uc9c0 \uc2e4\uc218 \ud328\ud134\uacfc \uc194\ub8e8\uc158 (\ud83c\uddf0\ud83c\uddf7 edition: paper or digital)

    • Yes24.com

Covers (English, Japanese, Chinese, and Korean)"},{"location":"book/#about-the-author","title":"About the Author","text":"

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

"},{"location":"chapter-1/","title":"Go: Simple to learn but hard to master","text":"

This chapter covers

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

Making mistakes is part of everyone\u2019s life. As Albert Einstein once said,

Albert Einstein

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

What matters in the end isn\u2019t 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\u2019t a magical process; it involves making many mistakes and learning from them. The purpose of this book is centered around this idea. It will help you, the reader, become a more proficient Go developer by looking at and learning from 100 common mistakes people make in many areas of the language.

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

"},{"location":"chapter-1/#go-outline","title":"Go outline","text":"

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

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

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

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

Go FAQ

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

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

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

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

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

"},{"location":"chapter-1/#simple-doesnt-mean-easy","title":"Simple doesn\u2019t mean easy","text":"

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

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

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

It\u2019s not a question of confronting message passing versus sharing memory and determining the winner. However, it\u2019s 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\u2019t an easy topic in practice.

This leitmotif\u2014simple doesn\u2019t mean easy\u2014can be generalized to many aspects of Go, not only concurrency. Hence, to be proficient Go developers, we must have a thorough understanding of many aspects of the language, which requires time, effort, and mistakes.

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

"},{"location":"chapter-1/#100-go-mistakes","title":"100 Go mistakes","text":"

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

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

To strengthen this facilitative effect, this book accompanies each mistake as much as possible with real-world examples. This book isn\u2019t 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.

Unknown

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

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

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

We introduce each mistake category next.

"},{"location":"chapter-1/#bugs","title":"Bugs","text":"

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

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

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

"},{"location":"chapter-1/#needless-complexity","title":"Needless complexity","text":"

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

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

"},{"location":"chapter-1/#weaker-readability","title":"Weaker readability","text":"

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

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

"},{"location":"chapter-1/#suboptimal-or-unidiomatic-organization","title":"Suboptimal or unidiomatic organization","text":"

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\u2019ll 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.

"},{"location":"chapter-1/#lack-of-api-convenience","title":"Lack of API convenience","text":"

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

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

"},{"location":"chapter-1/#under-optimized-code","title":"Under-optimized code","text":"

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

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

"},{"location":"chapter-1/#lack-of-productivity","title":"Lack of productivity","text":"

In most cases, what\u2019s the best language we can choose when working on a new project? The one we\u2019re the most productive with. Being comfortable with how a language works and exploiting it to get the best out of it is crucial to reach proficiency.

In this book, we will cover many cases and concrete examples that will help us to be more productive while working in Go. For instance, we\u2019ll 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\u2019s time to delve into those 100 common Go mistakes.

"},{"location":"chapter-1/#summary","title":"Summary","text":"
  • Go is a modern programming language that enables developer productivity, which is crucial for most companies today.
  • Go is simple to learn but not easy to master. This is why we need to deepen our knowledge to make the most effective use of the language.
  • Learning via mistakes and concrete examples is a powerful way to be proficient in a language. This book will accelerate our path to proficiency by exploring 100 common mistakes.
  1. J. S. Moser, H. S. Schroder, et al., \u201cMind Your Errors: Evidence for a Neural Mechanism Linking Growth Mindset to Adaptive Posterror Adjustments,\u201d Psychological Science, vol. 22, no. 12, pp. 1484\u20131489, Dec. 2011.\u00a0\u21a9

  2. J. Metcalfe, \u201cLearning from Errors,\u201d Annual Review of Psychology, vol. 68, pp. 465\u2013489, Jan. 2017.\u00a0\u21a9

  3. Synopsys, \u201cThe Cost of Poor Software Quality in the US: A 2020 Report.\u201d 2020. https://news.synopsys.com/2021-01-06-Synopsys-Sponsored-CISQ-Research-Estimates-Cost-of-Poor-Software-Quality-in-the-US-2-08-Trillion-in-2020.\u00a0\u21a9

"},{"location":"external/","title":"External Resources","text":""},{"location":"external/#english","title":"English","text":""},{"location":"external/#the-best-golang-book-prime-reacts","title":"The Best Golang Book | Prime Reacts","text":""},{"location":"external/#book-review-100-go-mistakes-and-how-to-avoid-them","title":"Book Review: 100 Go Mistakes (And How to Avoid Them)","text":"

Post

"},{"location":"external/#the-most-useful-book-for-a-go-programmer","title":"The Most Useful Book for a Go Programmer?","text":""},{"location":"external/#how-to-make-mistakes-in-go-go-time-190","title":"How to make mistakes in Go - Go Time #190","text":"
  • Episode
  • Spotify
"},{"location":"external/#go-is-amazing","title":"Go is AMAZING","text":""},{"location":"external/#8lu-100-test-coverage","title":"8LU - 100% Test Coverage","text":""},{"location":"external/#some-tips-i-learned-from-100-mistakes-in-go","title":"Some Tips I learned from 100 Mistakes in Go","text":"

Post

"},{"location":"external/#what-can-be-summarized-from-100-go-mistakes","title":"What can be summarized from 100 Go Mistakes?","text":"

Post

"},{"location":"external/#book-review-100-go-mistakes-and-how-to-avoid-them_1","title":"Book review: 100 Go Mistakes and How to Avoid Them","text":"

Post

"},{"location":"external/#chinese","title":"Chinese","text":""},{"location":"external/#100-go-mistakes-and-how-to-avoid-them","title":"\u6df1\u5ea6\u9605\u8bfb\u4e4b\u300a100 Go Mistakes and How to Avoid Them","text":"

Post

"},{"location":"external/#100-go-mistakes","title":"100 Go Mistakes \u968f\u8bb0","text":"

Post

"},{"location":"external/#go","title":"\u6211\u4e3a\u4ec0\u4e48\u653e\u5f03Go\u8bed\u8a00\uff1f","text":"

Post

"},{"location":"external/#japanese","title":"Japanese","text":""},{"location":"external/#go100-go-mistakes-and-how-to-avoid-them","title":"\u6700\u8fd1\u8aad\u3093\u3060Go\u8a00\u8a9e\u306e\u672c\u306e\u7d39\u4ecb\uff1a100 Go Mistakes and How to Avoid Them","text":"

Post

"},{"location":"external/#100-go-mistakes-and-how-to-avoid-them_1","title":"\u300e100 Go Mistakes and How to Avoid Them\u300f\u3092\u8aad\u3080","text":"

Post

"},{"location":"external/#portuguese","title":"Portuguese","text":""},{"location":"external/#um-otimo-livro-para-programadores-go","title":"Um \u00d3TIMO livro para programadores Go","text":""},{"location":"ja/","title":"Go\u8a00\u8a9e\u3067\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044","text":"

\u3053\u306e\u30da\u30fc\u30b8\u306f\u300e100 Go Mistakes\u300f\u306e\u5185\u5bb9\u3092\u307e\u3068\u3081\u305f\u3082\u306e\u3067\u3059\u3002\u4e00\u65b9\u3067\u3001\u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u306b\u958b\u304b\u308c\u305f\u30da\u30fc\u30b8\u3067\u3082\u3042\u308a\u307e\u3059\u3002\u300c\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044\u300d\u304c\u65b0\u305f\u306b\u8ffd\u52a0\u3055\u308c\u308b\u3079\u304d\u3060\u3068\u304a\u8003\u3048\u3067\u3057\u305f\u3089 community mistake issue \u3092\u4f5c\u6210\u3057\u3066\u304f\u3060\u3055\u3044\u3002

Jobs

Is your company hiring? Sponsor the Japanese version of this repository and let a significant audience of Go developers (~1k unique visitors per week) know about your opportunities in this section.

\u6ce8\u610f

\u73fe\u5728\u3001\u5927\u5e45\u306b\u591a\u304f\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u3092\u8ffd\u52a0\u3057\u3066\u5f37\u5316\u3057\u3066\u3044\u308b\u65b0\u3057\u3044\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u95b2\u89a7\u3057\u3066\u3044\u307e\u3059\u3002\u3053\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u306f\u307e\u3060\u958b\u767a\u4e2d\u3067\u3059\u3002\u554f\u984c\u3092\u898b\u3064\u3051\u305f\u5834\u5408\u306f\u3069\u3046\u305e\u6c17\u8efd\u306bPR\u3092\u4f5c\u6210\u3057\u3066\u304f\u3060\u3055\u3044\u3002

"},{"location":"ja/#_1","title":"\u30b3\u30fc\u30c9\u3068\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u69cb\u6210","text":""},{"location":"ja/#1","title":"\u610f\u56f3\u7684\u3067\u306a\u3044\u5909\u6570\u306e\u30b7\u30e3\u30c9\u30fc\u30a4\u30f3\u30b0 (#1)","text":"\u8981\u7d04

\u5909\u6570\u306e\u30b7\u30e3\u30c9\u30fc\u30a4\u30f3\u30b0\u3092\u907f\u3051\u308b\u3053\u3068\u306f\u3001\u8aa4\u3063\u305f\u5909\u6570\u306e\u53c2\u7167\u3084\u8aad\u307f\u624b\u306e\u6df7\u4e71\u3092\u9632\u304e\u307e\u3059\u3002

\u5909\u6570\u306e\u30b7\u30e3\u30c9\u30fc\u30a4\u30f3\u30b0\u306f\u3001\u5909\u6570\u540d\u304c\u30d6\u30ed\u30c3\u30af\u5185\u3067\u518d\u5ba3\u8a00\u3055\u308c\u308b\u3053\u3068\u3067\u751f\u3058\u307e\u3059\u304c\u3001\u3053\u308c\u306f\u9593\u9055\u3044\u3092\u5f15\u304d\u8d77\u3053\u3057\u3084\u3059\u304f\u3057\u307e\u3059\u3002\u5909\u6570\u306e\u30b7\u30e3\u30c9\u30fc\u30a4\u30f3\u30b0\u3092\u7981\u6b62\u3059\u308b\u304b\u3069\u3046\u304b\u306f\u500b\u4eba\u306e\u597d\u307f\u306b\u3088\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u30a8\u30e9\u30fc\u306b\u5bfe\u3057\u3066 err \u306e\u3088\u3046\u306a\u65e2\u5b58\u306e\u5909\u6570\u540d\u3092\u518d\u5229\u7528\u3059\u308b\u3068\u4fbf\u5229\u306a\u5834\u5408\u304c\u3042\u308a\u307e\u3059\u3002\u3068\u306f\u3044\u3048\u3001\u30b3\u30fc\u30c9\u306f\u30b3\u30f3\u30d1\u30a4\u30eb\u3055\u308c\u305f\u3082\u306e\u306e\u3001\u5024\u3092\u53d7\u3051\u53d6\u3063\u305f\u5909\u6570\u304c\u4e88\u671f\u3057\u305f\u3082\u306e\u3067\u306f\u306a\u3044\u3068\u3044\u3046\u30b7\u30ca\u30ea\u30aa\u306b\u76f4\u9762\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u305f\u3081\u3001\u539f\u5247\u3068\u3057\u3066\u5f15\u304d\u7d9a\u304d\u6ce8\u610f\u3092\u6255\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#2","title":"\u4e0d\u5fc5\u8981\u306b\u30cd\u30b9\u30c8\u3055\u308c\u305f\u30b3\u30fc\u30c9 (#2)","text":"\u8981\u7d04

\u30cd\u30b9\u30c8\u304c\u6df1\u304f\u306a\u3089\u306a\u3044\u3088\u3046\u306b\u3057\u3001\u30cf\u30c3\u30d4\u30fc\u30d1\u30b9\u3092\u5de6\u5074\u306b\u63c3\u3048\u308b\u3053\u3068\u3067\u30e1\u30f3\u30bf\u30eb\u30b3\u30fc\u30c9\u30e2\u30c7\u30eb\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u5bb9\u6613\u306b\u306a\u308a\u307e\u3059\u3002

\u4e00\u822c\u7684\u306b\u3001\u95a2\u6570\u304c\u3088\u308a\u6df1\u3044\u30cd\u30b9\u30c8\u3092\u8981\u6c42\u3059\u308b\u307b\u3069\u3001\u8aad\u3093\u3067\u7406\u89e3\u3059\u308b\u3053\u3068\u304c\u3088\u308a\u8907\u96d1\u306b\u306a\u308a\u307e\u3059\u3002\u79c1\u305f\u3061\u306e\u30b3\u30fc\u30c9\u306e\u53ef\u8aad\u6027\u3092\u6700\u9069\u5316\u3059\u308b\u305f\u3081\u306b\u3001\u3053\u306e\u30eb\u30fc\u30eb\u306e\u9069\u7528\u65b9\u6cd5\u3092\u898b\u3066\u3044\u304d\u307e\u3057\u3087\u3046\u3002

  • if \u30d6\u30ed\u30c3\u30af\u304c\u8fd4\u3055\u308c\u308b\u3068\u304d\u3001\u3059\u3079\u3066\u306e\u5834\u5408\u306b\u304a\u3044\u3066 else \u30d6\u30ed\u30c3\u30af\u3092\u7701\u7565\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 \u305f\u3068\u3048\u3070\u3001\u6b21\u306e\u3088\u3046\u306b\u66f8\u304f\u3079\u304d\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002
if foo() {\n    // ...\n    return true\n} else {\n    // ...\n}\n

\u4ee3\u308f\u308a\u306b\u3001\u6b21\u306e\u3088\u3046\u306b else \u30d6\u30ed\u30c3\u30af\u3092\u7701\u7565\u3057\u307e\u3059\u3002

if foo() {\n    // ...\n    return true\n}\n// ...\n
  • \u30ce\u30f3\u30cf\u30c3\u30d4\u30fc\u30d1\u30b9\u3067\u3082\u3053\u306e\u30ed\u30b8\u30c3\u30af\u306b\u5f93\u3046\u3053\u3068\u304c\u53ef\u80fd\u3067\u3059\u3002
if s != \"\" {\n    // ...\n} else {\n    return errors.New(\"empty string\")\n}\n

\u3053\u3053\u3067\u306f\u3001\u7a7a\u306e s \u304c\u30ce\u30f3\u30cf\u30c3\u30d4\u30fc\u30d1\u30b9\u3092\u8868\u3057\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u6b21\u306e\u3088\u3046\u306b\u6761\u4ef6\u3092\u3072\u3063\u304f\u308a\u8fd4\u3059\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

if s == \"\" {\n    return errors.New(\"empty string\")\n}\n// ...\n

\u8aad\u307f\u3084\u3059\u3044\u30b3\u30fc\u30c9\u3092\u66f8\u304f\u3053\u3068\u306f\u3001\u3059\u3079\u3066\u306e\u958b\u767a\u8005\u306b\u3068\u3063\u3066\u91cd\u8981\u306a\u8ab2\u984c\u3067\u3059\u3002\u30cd\u30b9\u30c8\u3055\u308c\u305f\u30d6\u30ed\u30c3\u30af\u306e\u6570\u3092\u6e1b\u3089\u3059\u3088\u3046\u52aa\u3081\u3001\u30cf\u30c3\u30d4\u30fc\u30d1\u30b9\u3092\u5de6\u5074\u306b\u63c3\u3048\u3001\u3067\u304d\u308b\u3060\u3051\u65e9\u304f\u623b\u308b\u3053\u3068\u304c\u3001\u30b3\u30fc\u30c9\u306e\u53ef\u8aad\u6027\u3092\u5411\u4e0a\u3055\u305b\u308b\u5177\u4f53\u7684\u306a\u624b\u6bb5\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#init-3","title":"init\u95a2\u6570\u306e\u8aa4\u7528 (#3)","text":"\u8981\u7d04

\u5909\u6570\u3092\u521d\u671f\u5316\u3059\u308b\u3068\u304d\u306f\u3001init\u95a2\u6570\u306e\u30a8\u30e9\u30fc\u51e6\u7406\u304c\u5236\u9650\u3055\u308c\u3066\u304a\u308a\u3001\u30b9\u30c6\u30fc\u30c8\u306e\u51e6\u7406\u3068\u30c6\u30b9\u30c8\u304c\u3088\u308a\u8907\u96d1\u306b\u306a\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u3001\u521d\u671f\u5316\u306f\u7279\u5b9a\u306e\u95a2\u6570\u3068\u3057\u3066\u51e6\u7406\u3055\u308c\u308b\u3079\u304d\u3067\u3059\u3002

init\u95a2\u6570\u306f\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30b9\u30c6\u30fc\u30c8\u3092\u521d\u671f\u5316\u3059\u308b\u305f\u3081\u306b\u4f7f\u7528\u3055\u308c\u308b\u95a2\u6570\u3067\u3059\u3002\u5f15\u6570\u3092\u53d6\u3089\u305a\u3001\u7d50\u679c\u3082\u8fd4\u3057\u307e\u305b\u3093\uff08 func() \u95a2\u6570\uff09\u3002\u30d1\u30c3\u30b1\u30fc\u30b8\u304c\u521d\u671f\u5316\u3055\u308c\u308b\u3068\u3001\u30d1\u30c3\u30b1\u30fc\u30b8\u5185\u306e\u3059\u3079\u3066\u306e\u5b9a\u6570\u304a\u3088\u3073\u5909\u6570\u306e\u5ba3\u8a00\u304c\u8a55\u4fa1\u3055\u308c\u307e\u3059\u3002\u6b21\u306b\u3001init\u95a2\u6570\u304c\u5b9f\u884c\u3055\u308c\u307e\u3059\u3002

init\u95a2\u6570\u306f\u3044\u304f\u3064\u304b\u306e\u554f\u984c\u3092\u5f15\u304d\u8d77\u3053\u3059\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002

  • \u30a8\u30e9\u30fc\u51e6\u7406\u304c\u5236\u9650\u3055\u308c\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002
  • \u30c6\u30b9\u30c8\u306e\u5b9f\u88c5\u65b9\u6cd5\u304c\u8907\u96d1\u306b\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\uff08\u305f\u3068\u3048\u3070\u3001\u5916\u90e8\u4f9d\u5b58\u95a2\u4fc2\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u304c\u3001\u5358\u4f53\u30c6\u30b9\u30c8\u306e\u7bc4\u56f2\u3067\u306f\u5fc5\u8981\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\uff09\u3002
  • \u521d\u671f\u5316\u3067\u30b9\u30c6\u30fc\u30c8\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306f\u3001\u30b0\u30ed\u30fc\u30d0\u30eb\u5909\u6570\u3092\u4f7f\u7528\u3057\u3066\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

init\u95a2\u6570\u306b\u306f\u6ce8\u610f\u304c\u5fc5\u8981\u3067\u3059\u3002\u305f\u3060\u3057\u3001\u9759\u7684\u69cb\u6210\u306e\u5b9a\u7fa9\u306a\u3069\u3001\u72b6\u6cc1\u306b\u3088\u3063\u3066\u306f\u5f79\u7acb\u3064\u5834\u5408\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u308c\u4ee5\u5916\u306e\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u3001\u521d\u671f\u5316\u51e6\u7406\u306f\u7279\u5b9a\u306e\u95a2\u6570\u3092\u901a\u3058\u3066\u884c\u308f\u308c\u308b\u3079\u304d\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#4","title":"\u30b2\u30c3\u30bf\u30fc\u3068\u30bb\u30c3\u30bf\u30fc\u306e\u4e71\u7528 (#4)","text":"\u8981\u7d04

Go\u8a00\u8a9e\u3067\u306f\u3001\u6163\u7528\u7684\u306b\u30b2\u30c3\u30bf\u30fc\u3068\u30bb\u30c3\u30bf\u30fc\u306e\u4f7f\u7528\u3092\u5f37\u5236\u3059\u308b\u3053\u3068\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u5b9f\u5229\u3092\u91cd\u8996\u3057\u3001\u52b9\u7387\u6027\u3068\u7279\u5b9a\u306e\u6163\u7fd2\u306b\u5f93\u3046\u3053\u3068\u3068\u306e\u9593\u306e\u9069\u5207\u306a\u30d0\u30e9\u30f3\u30b9\u3092\u898b\u3064\u3051\u308b\u3053\u3068\u304c\u3001\u9032\u3080\u3079\u304d\u9053\u3067\u3042\u308b\u306f\u305a\u3067\u3059\u3002

\u30c7\u30fc\u30bf\u306e\u30ab\u30d7\u30bb\u30eb\u5316\u3068\u306f\u3001\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u5024\u307e\u305f\u306f\u72b6\u614b\u3092\u96a0\u3059\u3053\u3068\u3092\u6307\u3057\u307e\u3059\u3002\u30b2\u30c3\u30bf\u30fc\u3068\u30bb\u30c3\u30bf\u30fc\u306f\u3001\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u4e0a\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u305f\u30e1\u30bd\u30c3\u30c9\u3092\u63d0\u4f9b\u3059\u308b\u3053\u3068\u3067\u30ab\u30d7\u30bb\u30eb\u5316\u3092\u53ef\u80fd\u306b\u3059\u308b\u624b\u6bb5\u3067\u3059\u3002

Go\u8a00\u8a9e\u3067\u306f\u3001\u4e00\u90e8\u306e\u8a00\u8a9e\u3067\u898b\u3089\u308c\u308b\u3088\u3046\u306a\u30b2\u30c3\u30bf\u30fc\u3068\u30bb\u30c3\u30bf\u30fc\u306e\u81ea\u52d5\u30b5\u30dd\u30fc\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u307e\u305f\u3001\u30b2\u30c3\u30bf\u30fc\u3068\u30bb\u30c3\u30bf\u30fc\u3092\u4f7f\u7528\u3057\u3066\u69cb\u9020\u4f53\u30d5\u30a3\u30fc\u30eb\u30c9\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u3053\u3068\u306f\u5fc5\u9808\u3067\u3082\u6163\u7528\u7684\u3067\u3082\u3042\u308a\u307e\u305b\u3093\u3002\u5024\u3092\u3082\u305f\u3089\u3055\u306a\u3044\u69cb\u9020\u4f53\u306e\u30b2\u30c3\u30bf\u30fc\u3068\u30bb\u30c3\u30bf\u30fc\u3067\u30b3\u30fc\u30c9\u3092\u57cb\u3081\u308b\u3079\u304d\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u5b9f\u5229\u3092\u91cd\u8996\u3057\u3001\u4ed6\u306e\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u30d1\u30e9\u30c0\u30a4\u30e0\u3067\u6642\u306b\u306f\u8b70\u8ad6\u306e\u4f59\u5730\u304c\u306a\u3044\u3068\u8003\u3048\u3089\u308c\u3066\u3044\u308b\u6163\u7fd2\u306b\u5f93\u3046\u3053\u3068\u3068\u3001\u52b9\u7387\u6027\u3068\u306e\u9593\u306e\u9069\u5207\u306a\u30d0\u30e9\u30f3\u30b9\u3092\u898b\u3064\u3051\u308b\u3088\u3046\u52aa\u3081\u308b\u3079\u304d\u3067\u3059\u3002

Go\u8a00\u8a9e\u306f\u3001\u30b7\u30f3\u30d7\u30eb\u3055\u3092\u542b\u3080\u591a\u304f\u306e\u7279\u6027\u3092\u8003\u616e\u3057\u3066\u8a2d\u8a08\u3055\u308c\u305f\u72ec\u81ea\u306e\u8a00\u8a9e\u3067\u3042\u308b\u3053\u3068\u3092\u5fd8\u308c\u306a\u3044\u3067\u304f\u3060\u3055\u3044\u3002\u305f\u3060\u3057\u3001\u30b2\u30c3\u30bf\u30fc\u3068\u30bb\u30c3\u30bf\u30fc\u306e\u5fc5\u8981\u6027\u304c\u898b\u3064\u304b\u3063\u305f\u5834\u5408\u3001\u307e\u305f\u306f\u524d\u8ff0\u306e\u3088\u3046\u306b\u3001\u524d\u65b9\u4e92\u63db\u6027\u3092\u4fdd\u8a3c\u3057\u306a\u304c\u3089\u5c06\u6765\u306e\u5fc5\u8981\u6027\u304c\u4e88\u6e2c\u3055\u308c\u308b\u5834\u5408\u306f\u3001\u305d\u308c\u3089\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u306b\u554f\u984c\u306f\u3042\u308a\u307e\u305b\u3093\u3002

"},{"location":"ja/#5","title":"\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u6c5a\u67d3 (#5)","text":"\u8981\u7d04

\u62bd\u8c61\u5316\u306f\u4f5c\u6210\u3055\u308c\u308b\u3079\u304d\u3082\u306e\u3067\u306f\u306a\u304f\u3001\u767a\u898b\u3055\u308c\u308b\u3079\u304d\u3082\u306e\u3067\u3059\u3002\u4e0d\u5fc5\u8981\u306a\u8907\u96d1\u3055\u3092\u907f\u3051\u308b\u305f\u3081\u306b\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306f\u3001\u5fc5\u8981\u306b\u306a\u308b\u3068\u4e88\u6e2c\u3057\u305f\u3068\u304d\u3067\u306f\u306a\u304f\u3001\u5fc5\u8981\u306b\u306a\u3063\u305f\u3068\u304d\u306b\u4f5c\u6210\u3059\u308b\u304b\u3001\u5c11\u306a\u304f\u3068\u3082\u62bd\u8c61\u5316\u304c\u6709\u52b9\u3067\u3042\u308b\u3053\u3068\u3092\u8a3c\u660e\u3067\u304d\u308b\u5834\u5408\u306b\u4f5c\u6210\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306f\u3001\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u52d5\u4f5c\u3092\u6307\u5b9a\u3059\u308b\u65b9\u6cd5\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002\u8907\u6570\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u304c\u5b9f\u88c5\u3067\u304d\u308b\u5171\u901a\u9805\u3092\u62bd\u51fa\u3059\u308b\u305f\u3081\u306b\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306f\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002Go\u8a00\u8a9e\u306e\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u304c\u5927\u304d\u304f\u7570\u306a\u308b\u306e\u306f\u3001\u6697\u9ed9\u7684\u306b\u6e80\u305f\u3055\u308c\u308b\u3053\u3068\u3067\u3059\u3002\u30aa\u30d6\u30b8\u30a7\u30af\u30c8 X \u304c\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9 Y \u3092\u5b9f\u88c5\u3057\u3066\u3044\u308b\u3053\u3068\u3092\u793a\u3059 implements \u306e\u3088\u3046\u306a\u660e\u793a\u7684\u306a\u30ad\u30fc\u30ef\u30fc\u30c9\u306f\u3042\u308a\u307e\u305b\u3093\u3002

\u4e00\u822c\u306b\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u304c\u4fa1\u5024\u3092\u3082\u305f\u3089\u3059\u3068\u8003\u3048\u3089\u308c\u308b\u4e3b\u8981\u306a\u4f7f\u7528\u4f8b\u306f\uff13\u3064\u3042\u308a\u307e\u3059\u3002\u305d\u308c\u306f\u3001\u5171\u901a\u306e\u52d5\u4f5c\u3092\u9664\u5916\u3059\u308b\u3001\u4f55\u3089\u304b\u306e\u5206\u96e2\u3092\u4f5c\u6210\u3059\u308b\u3001\u304a\u3088\u3073\u578b\u3092\u7279\u5b9a\u306e\u52d5\u4f5c\u306b\u5236\u9650\u3059\u308b\u3068\u3044\u3046\u3082\u306e\u3067\u3059\u3002\u305f\u3060\u3057\u3001\u3053\u306e\u30ea\u30b9\u30c8\u306f\u3059\u3079\u3066\u3092\u7db2\u7f85\u3057\u3066\u3044\u308b\u308f\u3051\u3067\u306f\u306a\u304f\u3001\u76f4\u9762\u3059\u308b\u72b6\u6cc1\u306b\u3088\u3063\u3066\u3082\u7570\u306a\u308a\u307e\u3059\u3002

\u591a\u304f\u306e\u5834\u5408\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306f\u62bd\u8c61\u5316\u3059\u308b\u305f\u3081\u306b\u4f5c\u6210\u3055\u308c\u307e\u3059\u3002\u305d\u3057\u3066\u3001\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u3067\u62bd\u8c61\u5316\u3059\u308b\u3068\u304d\u306e\u4e3b\u306a\u6ce8\u610f\u70b9\u306f\u3001\u62bd\u8c61\u5316\u306f\u4f5c\u6210\u3055\u308c\u308b\u3079\u304d\u3067\u306f\u306a\u304f\u3001\u767a\u898b\u3055\u308c\u308b\u3079\u304d\u3067\u3042\u308b\u3068\u3044\u3046\u3053\u3068\u3092\u899a\u3048\u3066\u304a\u304f\u3053\u3068\u3067\u3059\u3002\u3059\u306a\u308f\u3061\u3001\u305d\u3046\u3059\u308b\u76f4\u63a5\u306e\u7406\u7531\u304c\u306a\u3044\u9650\u308a\u3001\u30b3\u30fc\u30c9\u5185\u3067\u62bd\u8c61\u5316\u3059\u3079\u304d\u3067\u306f\u306a\u3044\u3068\u3044\u3046\u3053\u3068\u3067\u3059\u3002\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u4f7f\u3063\u3066\u8a2d\u8a08\u3059\u308b\u306e\u3067\u306f\u306a\u304f\u3001\u5177\u4f53\u7684\u306a\u30cb\u30fc\u30ba\u3092\u5f85\u3064\u3079\u304d\u3067\u3059\u3002\u5225\u306e\u8a00\u3044\u65b9\u3092\u3059\u308c\u3070\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306f\u5fc5\u8981\u306b\u306a\u308b\u3068\u4e88\u6e2c\u3057\u305f\u3068\u304d\u3067\u306f\u306a\u304f\u3001\u5fc5\u8981\u306b\u306a\u3063\u305f\u3068\u304d\u306b\u4f5c\u6210\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 \u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306e\u904e\u5ea6\u306a\u4f7f\u7528\u3092\u3057\u305f\u5834\u5408\u306e\u4e3b\u306a\u554f\u984c\u306f\u4f55\u3067\u3057\u3087\u3046\u304b\u3002\u7b54\u3048\u306f\u3001\u30b3\u30fc\u30c9\u30d5\u30ed\u30fc\u304c\u3088\u308a\u8907\u96d1\u306b\u306a\u308b\u3053\u3068\u3067\u3059\u3002\u5f79\u306b\u7acb\u305f\u306a\u3044\u9593\u63a5\u53c2\u7167\u3092\u8ffd\u52a0\u3057\u3066\u3082\u4f55\u306e\u4fa1\u5024\u3082\u3042\u308a\u307e\u305b\u3093\u3002\u305d\u308c\u306f\u4fa1\u5024\u306e\u306a\u3044\u62bd\u8c61\u5316\u3092\u3059\u308b\u3053\u3068\u3067\u3001\u30b3\u30fc\u30c9\u3092\u8aad\u307f\u3001\u7406\u89e3\u3057\u3001\u63a8\u8ad6\u3059\u308b\u3053\u3068\u3092\u3055\u3089\u306b\u56f0\u96e3\u306b\u3057\u307e\u3059\u3002\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u8ffd\u52a0\u3059\u308b\u660e\u78ba\u306a\u7406\u7531\u304c\u306a\u304f\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306b\u3088\u3063\u3066\u30b3\u30fc\u30c9\u304c\u3069\u306e\u3088\u3046\u306b\u6539\u5584\u3055\u308c\u308b\u304b\u304c\u4e0d\u660e\u77ad\u306a\u5834\u5408\u306f\u3001\u305d\u306e\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306e\u76ee\u7684\u306b\u7570\u8b70\u3092\u5531\u3048\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u5b9f\u88c5\u3092\u76f4\u63a5\u547c\u3073\u51fa\u3059\u306e\u3082\u4e00\u3064\u306e\u624b\u3067\u3059\u3002

\u30b3\u30fc\u30c9\u5185\u3067\u62bd\u8c61\u5316\u3059\u308b\u3068\u304d\u306f\u6ce8\u610f\u304c\u5fc5\u8981\u3067\u3059\uff08\u62bd\u8c61\u5316\u306f\u4f5c\u6210\u3059\u308b\u306e\u3067\u306f\u306a\u304f\u3001\u767a\u898b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\uff09\u3002\u5f8c\u3067\u5fc5\u8981\u306b\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u3082\u306e\u3092\u8003\u616e\u3057\u3001\u5b8c\u74a7\u306a\u62bd\u8c61\u5316\u30ec\u30d9\u30eb\u3092\u63a8\u6e2c\u3057\u3066\u3001\u79c1\u305f\u3061\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u958b\u767a\u8005\u306f\u30b3\u30fc\u30c9\u3092\u30aa\u30fc\u30d0\u30fc\u30a8\u30f3\u30b8\u30cb\u30a2\u30ea\u30f3\u30b0\u3059\u308b\u3053\u3068\u304c\u3088\u304f\u3042\u308a\u307e\u3059\u3002\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u3001\u30b3\u30fc\u30c9\u304c\u4e0d\u5fc5\u8981\u306a\u62bd\u8c61\u5316\u3067\u6c5a\u67d3\u3055\u308c\u3001\u8aad\u307f\u306b\u304f\u304f\u306a\u308b\u305f\u3081\u3001\u3053\u306e\u30d7\u30ed\u30bb\u30b9\u306f\u907f\u3051\u308b\u3079\u304d\u3067\u3059\u3002

\u30ed\u30d6\u30fb\u30d1\u30a4\u30af

\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3067\u30c7\u30b6\u30a4\u30f3\u3059\u308b\u306a\u3002\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u898b\u3064\u3051\u51fa\u305b\u3002

\u62bd\u8c61\u7684\u306b\u554f\u984c\u3092\u89e3\u6c7a\u3057\u3088\u3046\u3068\u3059\u308b\u306e\u3067\u306f\u306a\u304f\u3001\u4eca\u89e3\u6c7a\u3059\u3079\u304d\u3053\u3068\u3092\u89e3\u6c7a\u3057\u307e\u3057\u3087\u3046\u3002\u6700\u5f8c\u306b\u91cd\u8981\u306a\u3053\u3068\u3067\u3059\u304c\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306b\u3088\u3063\u3066\u30b3\u30fc\u30c9\u304c\u3069\u306e\u3088\u3046\u306b\u6539\u5584\u3055\u308c\u308b\u304b\u304c\u4e0d\u660e\u77ad\u306a\u5834\u5408\u306f\u3001\u30b3\u30fc\u30c9\u3092\u7c21\u7d20\u5316\u3059\u308b\u305f\u3081\u306b\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u524a\u9664\u3059\u308b\u3053\u3068\u3092\u691c\u8a0e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3067\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#6","title":"\u751f\u7523\u8005\u5074\u306e\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9 (#6)","text":"\u8981\u7d04

\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u5074\u3067\u4fdd\u6301\u3059\u308b\u3053\u3068\u3067\u4e0d\u5fc5\u8981\u306a\u62bd\u8c61\u5316\u3092\u56de\u907f\u3067\u304d\u307e\u3059\u3002

Go\u8a00\u8a9e\u3067\u306f\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u304c\u6697\u9ed9\u7684\u306b\u6e80\u305f\u3055\u308c\u307e\u3059\u3002\u3053\u308c\u306f\u3001\u660e\u793a\u7684\u306a\u5b9f\u88c5\u3092\u6301\u3064\u8a00\u8a9e\u3068\u6bd4\u8f03\u3057\u3066\u5927\u304d\u306a\u5909\u5316\u3092\u3082\u305f\u3089\u3059\u50be\u5411\u304c\u3042\u308a\u307e\u3059\u3002\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u3001\u5f93\u3046\u3079\u304d\u30a2\u30d7\u30ed\u30fc\u30c1\u306f\u524d\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u8aac\u660e\u3057\u305f\u3082\u306e\u2015\u2015\u62bd\u8c61\u5316\u306f\u4f5c\u6210\u3059\u308b\u306e\u3067\u306f\u306a\u304f\u3001\u767a\u898b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u2015\u2015\u306b\u4f3c\u3066\u3044\u307e\u3059\u3002\u3053\u308c\u306f\u3001\u3059\u3079\u3066\u306e\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u5bfe\u3057\u3066\u7279\u5b9a\u306e\u62bd\u8c61\u5316\u3092\u5f37\u5236\u3059\u308b\u306e\u306f\u751f\u7523\u8005\u306e\u5f79\u5272\u3067\u306f\u306a\u3044\u3053\u3068\u3092\u610f\u5473\u3057\u307e\u3059\u3002\u4ee3\u308f\u308a\u306b\u3001\u4f55\u3089\u304b\u306e\u5f62\u5f0f\u306e\u62bd\u8c61\u5316\u304c\u5fc5\u8981\u304b\u3069\u3046\u304b\u3092\u5224\u65ad\u3057\u3001\u305d\u306e\u30cb\u30fc\u30ba\u306b\u6700\u9069\u306a\u62bd\u8c61\u5316\u30ec\u30d9\u30eb\u3092\u6c7a\u5b9a\u3059\u308b\u306e\u306f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u8cac\u4efb\u3067\u3059\u3002

\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306f\u6d88\u8cbb\u8005\u5074\u306b\u5b58\u5728\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u7279\u5b9a\u306e\u72b6\u6cc1\uff08\u305f\u3068\u3048\u3070\u3001\u62bd\u8c61\u5316\u304c\u6d88\u8cbb\u8005\u306b\u3068\u3063\u3066\u5f79\u7acb\u3064\u3053\u3068\u304c\u308f\u304b\u3063\u3066\u3044\u308b\u2015\u2015\u4e88\u6e2c\u306f\u3057\u3066\u3044\u306a\u3044\u2015\u2015\u5834\u5408\uff09\u3067\u306f\u3001\u305d\u308c\u3092\u751f\u7523\u8005\u5074\u3067\u4f7f\u7528\u3057\u305f\u3044\u5834\u5408\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u3046\u3057\u305f\u5834\u5408\u3001\u53ef\u80fd\u306a\u9650\u308a\u6700\u5c0f\u9650\u306b\u6291\u3048\u3001\u518d\u5229\u7528\u53ef\u80fd\u6027\u3092\u9ad8\u3081\u3001\u3088\u308a\u7c21\u5358\u306b\u69cb\u6210\u3067\u304d\u308b\u3088\u3046\u306b\u52aa\u3081\u308b\u3079\u304d\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#7","title":"\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u8fd4\u3059 (#7)","text":"\u8981\u7d04

\u67d4\u8edf\u6027\u306b\u554f\u984c\u304c\u306a\u3044\u3088\u3046\u306b\u3059\u308b\u305f\u3081\u306b\u3001\u95a2\u6570\u306f\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3067\u306f\u306a\u304f\u5177\u4f53\u7684\u200b\u200b\u306a\u5b9f\u88c5\u3092\u8fd4\u3059\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u9006\u306b\u3001\u95a2\u6570\u306f\u53ef\u80fd\u306a\u9650\u308a\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u53d7\u3051\u5165\u308c\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3067\u306f\u306a\u304f\u5177\u4f53\u7684\u306a\u5b9f\u88c5\u3092\u8fd4\u3059\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u3046\u3067\u306a\u3044\u3068\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u4f9d\u5b58\u95a2\u4fc2\u306b\u3088\u308a\u8a2d\u8a08\u304c\u3044\u3063\u305d\u3046\u8907\u96d1\u306b\u306a\u308a\u3001\u3059\u3079\u3066\u306e\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u540c\u3058\u62bd\u8c61\u5316\u306b\u4f9d\u5b58\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u305f\u3081\u3001\u67d4\u8edf\u6027\u306b\u6b20\u3051\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u7d50\u8ad6\u306f\u524d\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3068\u4f3c\u3066\u3044\u307e\u3059\u3002\u62bd\u8c61\u5316\u304c\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u3068\u3063\u3066\u5f79\u7acb\u3064\u3053\u3068\u304c\uff08\u4e88\u6e2c\u3055\u308c\u308b\u3067\u306f\u306a\u304f\uff09\u308f\u304b\u3063\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u8fd4\u3059\u3053\u3068\u3092\u691c\u8a0e\u3057\u3066\u3082\u3088\u3044\u3067\u3057\u3087\u3046\u3002\u305d\u308c\u4ee5\u5916\u306e\u5834\u5408\u306f\u3001\u62bd\u8c61\u5316\u3092\u5f37\u5236\u3059\u3079\u304d\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u305d\u308c\u3089\u306f\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u3088\u3063\u3066\u767a\u898b\u3055\u308c\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u4f55\u3089\u304b\u306e\u7406\u7531\u3067\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u5b9f\u88c5\u3092\u62bd\u8c61\u5316\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u3067\u3082\u3001\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u5074\u3067\u305d\u308c\u3092\u884c\u3046\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

"},{"location":"ja/#any-8","title":"any \u306f\u4f55\u3082\u8a00\u308f\u306a\u3044 (#8)","text":"\u8981\u7d04

json.Marshal \u306a\u3069\u8003\u3048\u3046\u308b\u3059\u3079\u3066\u306e\u578b\u3092\u53d7\u3051\u5165\u308c\u308b\u304b\u8fd4\u3059\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306b\u306e\u307f any \u3092\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u305d\u308c\u4ee5\u5916\u306e\u5834\u5408\u3001any \u306f\u610f\u5473\u306e\u3042\u308b\u60c5\u5831\u3092\u63d0\u4f9b\u305b\u305a\u3001\u547c\u3073\u51fa\u3057\u5143\u304c\u4efb\u610f\u306e\u30c7\u30fc\u30bf\u578b\u306e\u30e1\u30bd\u30c3\u30c9\u3092\u547c\u3073\u51fa\u3059\u3053\u3068\u3092\u8a31\u53ef\u3059\u308b\u305f\u3081\u3001\u30b3\u30f3\u30d1\u30a4\u30eb\u6642\u306b\u554f\u984c\u304c\u767a\u751f\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002

any \u578b\u306f\u3001\u8003\u3048\u3046\u308b\u3059\u3079\u3066\u306e\u578b\u3092\u53d7\u3051\u5165\u308c\u308b\u304b\u8fd4\u3059\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\uff08\u305f\u3068\u3048\u3070\u3001\u30de\u30fc\u30b7\u30e3\u30ea\u30f3\u30b0\u3084\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u306e\u5834\u5408\uff09\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u539f\u5247\u3068\u3057\u3066\u30b3\u30fc\u30c9\u3092\u904e\u5ea6\u306b\u4e00\u822c\u5316\u3059\u308b\u3053\u3068\u306f\u4f55\u3068\u3057\u3066\u3082\u907f\u3051\u308b\u3079\u304d\u3067\u3059\u3002\u30b3\u30fc\u30c9\u306e\u8868\u73fe\u529b\u306a\u3069\u306e\u4ed6\u306e\u5074\u9762\u304c\u5411\u4e0a\u3059\u308b\u5834\u5408\u306f\u3001\u30b3\u30fc\u30c9\u3092\u5c11\u3057\u91cd\u8907\u3055\u305b\u305f\u307b\u3046\u304c\u826f\u3044\u3053\u3068\u3082\u3042\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#9","title":"\u30b8\u30a7\u30cd\u30ea\u30c3\u30af\u30b9\u3092\u3044\u3064\u4f7f\u7528\u3059\u308b\u3079\u304d\u304b\u7406\u89e3\u3057\u3066\u3044\u306a\u3044 (#9)","text":"\u8981\u7d04

\u30b8\u30a7\u30cd\u30ea\u30c3\u30af\u30b9\u3068\u578b\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u3092\u5229\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u8981\u7d20\u3084\u52d5\u4f5c\u3092\u9664\u5916\u3059\u308b\u305f\u3081\u306e\u30dc\u30a4\u30e9\u30fc\u30d7\u30ec\u30fc\u30c8\u30b3\u30fc\u30c9\u3092\u907f\u3051\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u578b\u30d1\u30e9\u30e1\u30fc\u30bf\u306f\u6642\u671f\u5c1a\u65e9\u306b\u4f7f\u7528\u305b\u305a\u3001\u5177\u4f53\u7684\u306a\u5fc5\u8981\u6027\u304c\u308f\u304b\u3063\u305f\u5834\u5408\u306b\u306e\u307f\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u305d\u3046\u3067\u306a\u3051\u308c\u3070\u3001\u4e0d\u5fc5\u8981\u306a\u62bd\u8c61\u5316\u3068\u8907\u96d1\u3055\u304c\u751f\u3058\u307e\u3059\u3002

\u30bb\u30af\u30b7\u30e7\u30f3\u5168\u6587\u306f\u3053\u3061\u3089\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#10","title":"\u578b\u306e\u57cb\u3081\u8fbc\u307f\u3067\u8d77\u3053\u308a\u3046\u308b\u554f\u984c\u3092\u628a\u63e1\u3057\u3066\u3044\u306a\u3044 (#10)","text":"\u8981\u7d04

\u578b\u57cb\u3081\u8fbc\u307f\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u30dc\u30a4\u30e9\u30fc\u30d7\u30ec\u30fc\u30c8\u30b3\u30fc\u30c9\u3092\u56de\u907f\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u305d\u3046\u3059\u308b\u3053\u3068\u3067\u3001\u4e00\u90e8\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u975e\u8868\u793a\u306b\u3057\u3066\u304a\u304f\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u306a\u3044\u3088\u3046\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u69cb\u9020\u4f53\u3092\u4f5c\u6210\u3059\u308b\u3068\u304d\u3001Go\u8a00\u8a9e\u306f\u578b\u3092\u57cb\u3081\u8fbc\u3080\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u578b\u57cb\u3081\u8fbc\u307f\u306e\u610f\u5473\u3092\u3059\u3079\u3066\u7406\u89e3\u3057\u3066\u3044\u306a\u3044\u3068\u3001\u4e88\u60f3\u5916\u306e\u52d5\u4f5c\u304c\u767a\u751f\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u578b\u3092\u57cb\u3081\u8fbc\u3080\u65b9\u6cd5\u3001\u305d\u308c\u304c\u3082\u305f\u3089\u3059\u3082\u306e\u3001\u304a\u3088\u3073\u8003\u3048\u3089\u308c\u308b\u554f\u984c\u306b\u3064\u3044\u3066\u898b\u3066\u3044\u304d\u307e\u3059\u3002

Go\u8a00\u8a9e\u3067\u306f\u3001\u540d\u524d\u306a\u3057\u3067\u5ba3\u8a00\u3055\u308c\u305f\u69cb\u9020\u4f53\u30d5\u30a3\u30fc\u30eb\u30c9\u306f\u3001\u57cb\u3081\u8fbc\u307f\u3068\u547c\u3070\u308c\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u6b21\u306e\u3088\u3046\u306a\u3082\u306e\u3067\u3059\u3002

type Foo struct {\n    Bar // \u57cb\u3081\u8fbc\u307f\u30d5\u30a3\u30fc\u30eb\u30c9\n}\n\ntype Bar struct {\n    Baz int\n}\n

Foo \u69cb\u9020\u4f53\u3067\u306f\u3001Bar \u578b\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f\u540d\u524d\u306a\u3057\u3067\u5ba3\u8a00\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u3053\u308c\u306f\u57cb\u3081\u8fbc\u307f\u30d5\u30a3\u30fc\u30eb\u30c9\u3067\u3059\u3002

\u57cb\u3081\u8fbc\u307f\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u57cb\u3081\u8fbc\u307f\u578b\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u3068\u30e1\u30bd\u30c3\u30c9\u306f\u6607\u683c\u3057\u307e\u3059\u3002Bar \u306b\u306f Baz \u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u542b\u307e\u308c\u3066\u3044\u308b\u305f\u3081\u3001\u3053\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u306f Foo \u306b\u6607\u683c\u3057\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001Foo \u304b\u3089 Baz \u3092\u5229\u7528\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002

\u578b\u306e\u57cb\u3081\u8fbc\u307f\u306b\u3064\u3044\u3066\u4f55\u304c\u8a00\u3048\u308b\u3067\u3057\u3087\u3046\u304b\u3002\u307e\u305a\u3001\u3053\u308c\u304c\u5fc5\u8981\u306b\u306a\u308b\u3053\u3068\u306f\u307b\u3068\u3093\u3069\u306a\u304f\u3001\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u304c\u4f55\u3067\u3042\u308c\u3001\u304a\u305d\u3089\u304f\u578b\u57cb\u3081\u8fbc\u307f\u306a\u3057\u3067\u3082\u540c\u69d8\u306b\u89e3\u6c7a\u3067\u304d\u308b\u3053\u3068\u3092\u610f\u5473\u3057\u307e\u3059\u3002\u578b\u306e\u57cb\u3081\u8fbc\u307f\u306f\u4e3b\u306b\u5229\u4fbf\u6027\u3092\u76ee\u7684\u3068\u3057\u3066\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u3001\u305d\u308c\u306f\u52d5\u4f5c\u3092\u6607\u683c\u3059\u308b\u305f\u3081\u306b\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002

\u578b\u57cb\u3081\u8fbc\u307f\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u306f\u3001\u6b21\u306e 2 \u3064\u306e\u4e3b\u306a\u5236\u7d04\u3092\u5ff5\u982d\u306b\u7f6e\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

  • \u30d5\u30a3\u30fc\u30eb\u30c9\u3078\u306e\u30a2\u30af\u30bb\u30b9\u3092\u7c21\u7d20\u5316\u3059\u308b\u305f\u3081\u306e\u7cd6\u8863\u69cb\u6587\u3068\u3057\u3066\u306e\u307f\u4f7f\u7528\u3057\u306a\u3044\u3067\u304f\u3060\u3055\u3044\uff08 Foo.Bar.Baz() \u306e\u4ee3\u308f\u308a\u306b Foo.Baz() \u306a\u3069\uff09\u3002 \u3053\u308c\u304c\u552f\u4e00\u306e\u6839\u62e0\u3067\u3042\u308b\u5834\u5408\u306f\u3001\u5185\u90e8\u578b\u3092\u57cb\u3081\u8fbc\u307e\u305a\u3001\u4ee3\u308f\u308a\u306b\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u4f7f\u3044\u307e\u3057\u3087\u3046\u3002
  • \u5916\u90e8\u304b\u3089\u96a0\u3057\u305f\u3044\u30c7\u30fc\u30bf\uff08\u30d5\u30a3\u30fc\u30eb\u30c9\uff09\u3084\u52d5\u4f5c\uff08\u30e1\u30bd\u30c3\u30c9\uff09\u3092\u6607\u683c\u3057\u3066\u306f\u306a\u308a\u307e\u305b\u3093\u3002\u305f\u3068\u3048\u3070\u3001\u69cb\u9020\u4f53\u306b\u5bfe\u3057\u3066\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8\u306a\u307e\u307e\u306b\u3057\u3066\u304a\u304f\u5fc5\u8981\u304c\u3042\u308b\u30ed\u30c3\u30af\u52d5\u4f5c\u306b\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u3088\u3046\u306b\u3059\u308b\u5834\u5408\u306a\u3069\u3067\u3059\u3002

\u3053\u308c\u3089\u306e\u5236\u7d04\u3092\u5ff5\u982d\u306b\u7f6e\u3044\u3066\u578b\u57cb\u3081\u8fbc\u307f\u3092\u610f\u8b58\u7684\u306b\u4f7f\u7528\u3059\u308b\u3068\u3001\u8ffd\u52a0\u306e\u8ee2\u9001\u30e1\u30bd\u30c3\u30c9\u306b\u3088\u308b\u30dc\u30a4\u30e9\u30fc\u30d7\u30ec\u30fc\u30c8\u30b3\u30fc\u30c9\u3092\u56de\u907f\u3059\u308b\u306e\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u898b\u305f\u76ee\u3060\u3051\u3092\u76ee\u7684\u3068\u3057\u305f\u308a\u3001\u96a0\u3059\u3079\u304d\u8981\u7d20\u3092\u6607\u683c\u3057\u305f\u308a\u3057\u306a\u3044\u3088\u3046\u306b\u6ce8\u610f\u3057\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#functional-options-11","title":"Functional Options \u30d1\u30bf\u30fc\u30f3\u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044 (#11)","text":"\u8981\u7d04

API \u306b\u9069\u3057\u305f\u65b9\u6cd5\u3067\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u4fbf\u5229\u306b\u51e6\u7406\u3059\u308b\u306b\u306f\u3001Functional Options \u30d1\u30bf\u30fc\u30f3\u3092\u4f7f\u7528\u3057\u307e\u3057\u3087\u3046\u3002

\u3055\u307e\u3056\u307e\u306a\u5b9f\u88c5\u65b9\u6cd5\u304c\u5b58\u5728\u3057\u3001\u591a\u5c11\u306e\u9055\u3044\u306f\u3042\u308a\u307e\u3059\u304c\u3001\u4e3b\u306a\u8003\u3048\u65b9\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u3059\u3002

  • \u672a\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u306e\u69cb\u9020\u4f53\u306f\u30aa\u30d7\u30b7\u30e7\u30f3\u8a2d\u5b9a\u3092\u4fdd\u6301\u3057\u307e\u3059\u3002
  • \u5404\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u540c\u3058\u578b\u3001type Option func(options *options) \u30a8\u30e9\u30fc\u3092\u8fd4\u3059\u95a2\u6570\u3067\u3059\u3002\u305f\u3068\u3048\u3070\u3001WithPort \u306f\u30dd\u30fc\u30c8\u3092\u8868\u3059 int \u5f15\u6570\u3092\u53d7\u3051\u53d6\u308a\u3001options \u69cb\u9020\u4f53\u306e\u66f4\u65b0\u65b9\u6cd5\u3092\u8868\u3059 Option \u578b\u3092\u8fd4\u3057\u307e\u3059\u3002

type options struct {\n  port *int\n}\n\ntype Option func(options *options) error\n\nfunc WithPort(port int) Option {\n  return func(options *options) error {\n    if port < 0 {\n    return errors.New(\"port should be positive\")\n  }\n  options.port = &port\n  return nil\n  }\n}\n\nfunc NewServer(addr string, opts ...Option) ( *http.Server, error) {\n  var options options\n  for _, opt := range opts { \n    err := opt(&options) \n    if err != nil {\n      return nil, err\n    }\n  }\n\n// \u3053\u306e\u6bb5\u968e\u3067\u3001options \u69cb\u9020\u4f53\u304c\u69cb\u7bc9\u3055\u308c\u3001\u69cb\u6210\u304c\u542b\u307e\u308c\u307e\u3059\u3002\n// \u3057\u305f\u304c\u3063\u3066\u3001\u30dd\u30fc\u30c8\u8a2d\u5b9a\u306b\u95a2\u9023\u3059\u308b\u30ed\u30b8\u30c3\u30af\u3092\u5b9f\u88c5\u3067\u304d\u307e\u3059\u3002\n  var port int\n  if options.port == nil {\n    port = defaultHTTPPort\n  } else {\n      if *options.port == 0 {\n      port = randomPort()\n    } else {\n      port = *options.port\n    }\n  }\n\n  // ...\n}\n

Functional Options \u30d1\u30bf\u30fc\u30f3\u306f\u3001\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306e\u624b\u8efd\u3067 API \u30d5\u30ec\u30f3\u30c9\u30ea\u30fc\u306a\u65b9\u6cd5\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002 Builder \u30d1\u30bf\u30fc\u30f3\u306f\u6709\u52b9\u306a\u30aa\u30d7\u30b7\u30e7\u30f3\u3067\u3059\u304c\u3001\u3044\u304f\u3064\u304b\u306e\u5c0f\u3055\u306a\u6b20\u70b9\uff08\u7a7a\u306e\u53ef\u80fd\u6027\u304c\u3042\u308b\u69cb\u6210\u69cb\u9020\u4f53\u3092\u6e21\u3055\u306a\u3051\u308c\u3070\u306a\u3089\u306a\u3044\u3001\u307e\u305f\u306f\u30a8\u30e9\u30fc\u3092\u51e6\u7406\u3059\u308b\u65b9\u6cd5\u304c\u3042\u307e\u308a\u4fbf\u5229\u3067\u306f\u306a\u3044\uff09\u304c\u3042\u308a\u3001\u3053\u306e\u7a2e\u306e\u554f\u984c\u306b\u304a\u3044\u3066 Functional Options \u30d1\u30bf\u30fc\u30f3\u304cGo\u8a00\u8a9e\u306b\u304a\u3051\u308b\u6163\u7528\u7684\u306a\u5bfe\u51e6\u65b9\u6cd5\u306b\u306a\u308b\u50be\u5411\u304c\u3042\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#12","title":"\u8aa4\u3063\u305f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u69cb\u6210 (\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u69cb\u9020\u3068\u30d1\u30c3\u30b1\u30fc\u30b8\u69cb\u6210) (#12)","text":"

\u5168\u4f53\u7684\u306a\u69cb\u6210\u306b\u95a2\u3057\u3066\u306f\u3001\u3055\u307e\u3056\u307e\u306a\u8003\u3048\u65b9\u304c\u3042\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3054\u3068\u306b\u6574\u7406\u3059\u3079\u304d\u304b\u3001\u305d\u308c\u3068\u3082\u30ec\u30a4\u30e4\u30fc\u3054\u3068\u306b\u6574\u7406\u3059\u3079\u304d\u304b\u3001\u305d\u308c\u306f\u597d\u307f\u306b\u3088\u3063\u3066\u7570\u306a\u308a\u307e\u3059\u3002\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\uff08\u9867\u5ba2\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3001\u5951\u7d04\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306a\u3069\uff09\u3054\u3068\u306b\u30b3\u30fc\u30c9\u3092\u30b0\u30eb\u30fc\u30d7\u5316\u3059\u308b\u3053\u3068\u3092\u9078\u3076\u5834\u5408\u3082\u3042\u308c\u3070\u3001\u516d\u89d2\u5f62\u306e\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u539f\u5247\u306b\u5f93\u3046\u3053\u3068\u3068\u3001\u6280\u8853\u5c64\u3054\u3068\u306b\u30b0\u30eb\u30fc\u30d7\u5316\u3059\u308b\u3053\u3068\u3092\u9078\u3076\u5834\u5408\u3082\u3042\u308a\u307e\u3059\u3002\u79c1\u305f\u3061\u304c\u884c\u3046\u6c7a\u5b9a\u304c\u4e00\u8cab\u3057\u3066\u3044\u308b\u9650\u308a\u3001\u305d\u308c\u304c\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u306b\u9069\u5408\u3059\u308b\u306a\u3089\u3001\u305d\u308c\u304c\u9593\u9055\u3063\u3066\u3044\u308b\u3053\u3068\u306f\u3042\u308a\u307e\u305b\u3093\u3002

\u30d1\u30c3\u30b1\u30fc\u30b8\u306b\u95a2\u3057\u3066\u306f\u3001\u5f93\u3046\u3079\u304d\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u304c\u8907\u6570\u3042\u308a\u307e\u3059\u3002\u307e\u305a\u3001\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u304c\u904e\u5ea6\u306b\u8907\u96d1\u306b\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u305f\u3081\u3001\u6642\u671f\u5c1a\u65e9\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u5316\u306f\u907f\u3051\u308b\u3079\u304d\u3067\u3059\u3002\u5834\u5408\u306b\u3088\u3063\u3066\u306f\u3001\u5b8c\u74a7\u306a\u69cb\u9020\u3092\u6700\u521d\u304b\u3089\u7121\u7406\u306b\u4f5c\u308d\u3046\u3068\u3059\u308b\u3088\u308a\u3082\u3001\u5358\u7d14\u306a\u69cb\u6210\u3092\u4f7f\u7528\u3057\u3001\u305d\u306e\u5185\u5bb9\u3092\u7406\u89e3\u3057\u305f\u4e0a\u3067\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u767a\u5c55\u3055\u305b\u308b\u307b\u3046\u304c\u826f\u3044\u5834\u5408\u304c\u3042\u308a\u307e\u3059\u3002 \u7c92\u5ea6\u3082\u8003\u616e\u3059\u3079\u304d\u91cd\u8981\u306a\u70b9\u3067\u3059\u3002 1 \u3064\u307e\u305f\u306f 2 \u3064\u306e\u30d5\u30a1\u30a4\u30eb\u3060\u3051\u3092\u542b\u3080\u6570\u5341\u306e\u30ca\u30ce\u30d1\u30c3\u30b1\u30fc\u30b8\u3092\u4f5c\u6210\u3059\u308b\u3053\u3068\u306f\u907f\u3051\u308b\u3079\u304d\u3067\u3059\u3002\u305d\u306e\u5834\u5408\u3001\u304a\u305d\u3089\u304f\u3053\u308c\u3089\u306e\u30d1\u30c3\u30b1\u30fc\u30b8\u9593\u306e\u8ad6\u7406\u7684\u306a\u63a5\u7d9a\u306e\u4e00\u90e8\u304c\u629c\u3051\u843d\u3061\u3001\u8aad\u307f\u624b\u306b\u3068\u3063\u3066\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u304c\u7406\u89e3\u3057\u306b\u304f\u304f\u306a\u308b\u304b\u3089\u3067\u3059\u3002\u9006\u306b\u3001\u30d1\u30c3\u30b1\u30fc\u30b8\u540d\u306e\u610f\u5473\u3092\u8584\u3081\u308b\u3088\u3046\u306a\u5de8\u5927\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u3082\u907f\u3051\u308b\u3079\u304d\u3067\u3059\u3002

\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u540d\u524d\u4ed8\u3051\u3082\u6ce8\u610f\u3057\u3066\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\uff08\u958b\u767a\u8005\u306a\u3089\uff09\u8ab0\u3082\u304c\u77e5\u3063\u3066\u3044\u308b\u3088\u3046\u306b\u3001\u540d\u524d\u3092\u4ed8\u3051\u308b\u306e\u306f\u96e3\u3057\u3044\u3067\u3059\u3002\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c Go \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u7406\u89e3\u3057\u3084\u3059\u3044\u3088\u3046\u306b\u3001\u30d1\u30c3\u30b1\u30fc\u30b8\u306b\u542b\u307e\u308c\u308b\u3082\u306e\u3067\u306f\u306a\u304f\u3001\u63d0\u4f9b\u3059\u308b\u3082\u306e\u306b\u57fa\u3065\u3044\u3066\u30d1\u30c3\u30b1\u30fc\u30b8\u306b\u540d\u524d\u3092\u4ed8\u3051\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u307e\u305f\u3001\u30cd\u30fc\u30df\u30f3\u30b0\u306b\u306f\u610f\u5473\u306e\u3042\u308b\u3082\u306e\u3092\u4ed8\u3051\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u30d1\u30c3\u30b1\u30fc\u30b8\u540d\u306f\u77ed\u304f\u3001\u7c21\u6f54\u3067\u3001\u8868\u73fe\u529b\u8c4a\u304b\u3067\u3001\u6163\u4f8b\u306b\u3088\u308a\u5358\u4e00\u306e\u5c0f\u6587\u5b57\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

\u4f55\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u304b\u306b\u3064\u3044\u3066\u306e\u30eb\u30fc\u30eb\u306f\u975e\u5e38\u306b\u7c21\u5358\u3067\u3059\u3002\u30d1\u30c3\u30b1\u30fc\u30b8\u9593\u306e\u7d50\u5408\u3092\u6e1b\u3089\u3057\u3001\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u308b\u4e0d\u8981\u306a\u8981\u7d20\u3092\u975e\u8868\u793a\u306b\u3059\u308b\u305f\u3081\u306b\u3001\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3082\u306e\u3092\u3067\u304d\u308b\u9650\u308a\u6700\u5c0f\u9650\u306b\u6291\u3048\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u8981\u7d20\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u304b\u3069\u3046\u304b\u4e0d\u660e\u306a\u5834\u5408\u306f\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3057\u306a\u3044\u3088\u3046\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u5f8c\u3067\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3053\u3068\u304c\u5224\u660e\u3057\u305f\u5834\u5408\u306f\u3001\u30b3\u30fc\u30c9\u3092\u8abf\u6574\u3067\u304d\u307e\u3059\u3002\u307e\u305f\u3001\u69cb\u9020\u4f53\u3092 encoding/json \u3067\u30a2\u30f3\u30de\u30fc\u30b7\u30e3\u30ea\u30f3\u30b0\u3067\u304d\u308b\u3088\u3046\u306b\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u306a\u3069\u3001\u3044\u304f\u3064\u304b\u306e\u4f8b\u5916\u306b\u3082\u7559\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u69cb\u6210\u3059\u308b\u306e\u306f\u7c21\u5358\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u304c\u3001\u3053\u308c\u3089\u306e\u30eb\u30fc\u30eb\u306b\u5f93\u3046\u3053\u3068\u3067\u7dad\u6301\u304c\u5bb9\u6613\u306b\u306a\u308a\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u4fdd\u5b88\u6027\u3092\u5bb9\u6613\u306b\u3059\u308b\u305f\u3081\u306b\u306f\u4e00\u8cab\u6027\u3082\u91cd\u8981\u3067\u3042\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u30b3\u30fc\u30c9\u30d9\u30fc\u30b9\u5185\u3067\u53ef\u80fd\u306a\u9650\u308a\u4e00\u8cab\u6027\u3092\u4fdd\u3064\u3088\u3046\u306b\u3057\u307e\u3057\u3087\u3046\u3002

\u88dc\u8db3

Go \u30c1\u30fc\u30e0\u306f Go \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u7d44\u7e54\u5316/\u69cb\u9020\u5316\u306b\u95a2\u3059\u308b\u516c\u5f0f\u30ac\u30a4\u30c9\u30e9\u30a4\u30f3\u3092 2023 \u5e74\u306b\u767a\u884c\u3057\u307e\u3057\u305f\uff1a go.dev/doc/modules/layout

"},{"location":"ja/#13","title":"\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u4f5c\u6210 (#13)","text":"\u8981\u7d04

\u540d\u524d\u4ed8\u3051\u306f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u8a2d\u8a08\u306e\u91cd\u8981\u306a\u90e8\u5206\u3067\u3059\u3002common \u3001util \u3001shared \u306e\u3088\u3046\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u3092\u4f5c\u6210\u3057\u3066\u3082\u3001\u8aad\u307f\u624b\u306b\u305d\u308c\u307b\u3069\u306e\u4fa1\u5024\u3092\u3082\u305f\u3089\u3057\u307e\u305b\u3093\u3002\u3053\u306e\u3088\u3046\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u3092\u610f\u5473\u306e\u3042\u308b\u5177\u4f53\u7684\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u540d\u306b\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\u3057\u307e\u3057\u3087\u3046\u3002

\u307e\u305f\u3001\u30d1\u30c3\u30b1\u30fc\u30b8\u306b\u542b\u307e\u308c\u308b\u3082\u306e\u3067\u306f\u306a\u304f\u3001\u30d1\u30c3\u30b1\u30fc\u30b8\u304c\u63d0\u4f9b\u3059\u308b\u3082\u306e\u306b\u57fa\u3065\u3044\u3066\u30d1\u30c3\u30b1\u30fc\u30b8\u306b\u540d\u524d\u3092\u4ed8\u3051\u308b\u3068\u3001\u305d\u306e\u8868\u73fe\u529b\u3092\u9ad8\u3081\u308b\u52b9\u7387\u7684\u306a\u65b9\u6cd5\u306b\u306a\u308b\u3053\u3068\u306b\u3082\u7559\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#14","title":"\u30d1\u30c3\u30b1\u30fc\u30b8\u540d\u306e\u885d\u7a81\u3092\u7121\u8996\u3059\u308b (#14)","text":"\u8981\u7d04

\u6df7\u4e71\u3001\u3055\u3089\u306b\u306f\u30d0\u30b0\u306b\u3064\u306a\u304c\u308a\u304b\u306d\u306a\u3044\u3001\u5909\u6570\u3068\u30d1\u30c3\u30b1\u30fc\u30b8\u9593\u306e\u540d\u524d\u306e\u885d\u7a81\u3092\u56de\u907f\u3059\u308b\u305f\u3081\u306b\u3001\u305d\u308c\u305e\u308c\u306b\u4e00\u610f\u306e\u540d\u524d\u3092\u4f7f\u7528\u3057\u307e\u3057\u3087\u3046\u3002\u3053\u308c\u304c\u4e0d\u53ef\u80fd\u306a\u5834\u5408\u306f\u3001\u30a4\u30f3\u30dd\u30fc\u30c8\u30a8\u30a4\u30ea\u30a2\u30b9\u3092\u4f7f\u7528\u3057\u3066\u4fee\u98fe\u5b50\u3092\u5909\u66f4\u3057\u3066\u30d1\u30c3\u30b1\u30fc\u30b8\u540d\u3068\u5909\u6570\u540d\u3092\u533a\u5225\u3059\u308b\u304b\u3001\u3088\u308a\u826f\u3044\u540d\u524d\u3092\u8003\u3048\u3066\u304f\u3060\u3055\u3044\u3002

\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u885d\u7a81\u306f\u3001\u5909\u6570\u540d\u304c\u65e2\u5b58\u306e\u30d1\u30c3\u30b1\u30fc\u30b8\u540d\u3068\u885d\u7a81\u3059\u308b\u5834\u5408\u306b\u767a\u751f\u3057\u3001\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u518d\u5229\u7528\u304c\u59a8\u3052\u3089\u308c\u307e\u3059\u3002\u66d6\u6627\u3055\u3092\u907f\u3051\u308b\u305f\u3081\u306b\u3001\u5909\u6570\u540d\u306e\u885d\u7a81\u3092\u9632\u3050\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u885d\u7a81\u304c\u767a\u751f\u3057\u305f\u5834\u5408\u306f\u3001\u5225\u306e\u610f\u5473\u306e\u3042\u308b\u540d\u524d\u3092\u898b\u3064\u3051\u308b\u304b\u3001\u30a4\u30f3\u30dd\u30fc\u30c8\u30a8\u30a4\u30ea\u30a2\u30b9\u3092\u4f7f\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

"},{"location":"ja/#15","title":"\u30b3\u30fc\u30c9\u306e\u6587\u7ae0\u5316\u304c\u884c\u308f\u308c\u3066\u3044\u306a\u3044 (#15)","text":"\u8981\u7d04

\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3068\u30e1\u30f3\u30c6\u30ca\u304c\u30b3\u30fc\u30c9\u306e\u610f\u56f3\u3092\u7406\u89e3\u3067\u304d\u308b\u3088\u3046\u306b\u3001\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u305f\u8981\u7d20\u3092\u6587\u7ae0\u5316\u3057\u307e\u3057\u3087\u3046\u3002

\u6587\u7ae0\u5316\u306f\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u306e\u91cd\u8981\u306a\u5074\u9762\u3067\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c API \u3092\u3088\u308a\u7c21\u5358\u306b\u4f7f\u7528\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u304c\u3001\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u7dad\u6301\u306b\u3082\u5f79\u7acb\u3061\u307e\u3059\u3002Go\u8a00\u8a9e\u3067\u306f\u3001\u30b3\u30fc\u30c9\u3092\u6163\u7528\u7684\u306a\u3082\u306e\u306b\u3059\u308b\u305f\u3081\u306b\u3001\u3044\u304f\u3064\u304b\u306e\u30eb\u30fc\u30eb\u306b\u5f93\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

\u307e\u305a\u3001\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u305f\u3059\u3079\u3066\u306e\u8981\u7d20\u3092\u6587\u7ae0\u5316\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u69cb\u9020\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3001\u95a2\u6570\u306a\u3069\u3001\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u5834\u5408\u306f\u6587\u7ae0\u5316\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u6163\u4f8b\u3068\u3057\u3066\u3001\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u305f\u8981\u7d20\u306e\u540d\u524d\u304b\u3089\u59cb\u307e\u308b\u30b3\u30e1\u30f3\u30c8\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002

\u6163\u4f8b\u3068\u3057\u3066\u3001\u5404\u30b3\u30e1\u30f3\u30c8\u306f\u53e5\u8aad\u70b9\u3067\u7d42\u308f\u308b\u5b8c\u5168\u306a\u6587\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u307e\u305f\u3001\u95a2\u6570\uff08\u307e\u305f\u306f\u30e1\u30bd\u30c3\u30c9\uff09\u3092\u6587\u7ae0\u5316\u3059\u308b\u3068\u304d\u306f\u3001\u95a2\u6570\u304c\u3069\u306e\u3088\u3046\u306b\u5b9f\u884c\u3059\u308b\u304b\u3067\u306f\u306a\u304f\u3001\u305d\u306e\u95a2\u6570\u304c\u4f55\u3092\u5b9f\u884c\u3059\u308b\u3064\u3082\u308a\u3067\u3042\u308b\u304b\u3092\u5f37\u8abf\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3053\u3068\u306b\u3082\u7559\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3053\u308c\u306f\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3067\u306f\u306a\u304f\u3001\u95a2\u6570\u3068\u30b3\u30e1\u30f3\u30c8\u306b\u3064\u3044\u3066\u3067\u3059\u3002\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306f\u7406\u60f3\u7684\u306b\u306f\u3001\u5229\u7528\u8005\u304c\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u305f\u8981\u7d20\u306e\u4f7f\u7528\u65b9\u6cd5\u3092\u7406\u89e3\u3059\u308b\u305f\u3081\u306b\u30b3\u30fc\u30c9\u3092\u898b\u308b\u5fc5\u8981\u304c\u306a\u3044\u307b\u3069\u5341\u5206\u306a\u60c5\u5831\u3092\u63d0\u4f9b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

\u5909\u6570\u307e\u305f\u306f\u5b9a\u6570\u3092\u6587\u7ae0\u5316\u3059\u308b\u5834\u5408\u3001\u305d\u306e\u76ee\u7684\u3068\u5185\u5bb9\u3068\u3044\u3046 2 \u3064\u306e\u5074\u9762\u3092\u4f1d\u3048\u308b\u3053\u3068\u304c\u91cd\u8981\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002\u524d\u8005\u306f\u3001\u5916\u90e8\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u3068\u3063\u3066\u5f79\u7acb\u3064\u3088\u3046\u306b\u3001\u30b3\u30fc\u30c9\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3068\u3057\u3066\u5b58\u5728\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u5f8c\u8005\u306f\u5fc5\u305a\u3057\u3082\u516c\u958b\u3055\u308c\u308b\u3079\u304d\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002

\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3068\u30e1\u30f3\u30c6\u30ca\u304c\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u76ee\u7684\u3092\u7406\u89e3\u3067\u304d\u308b\u3088\u3046\u306b\u3001\u5404\u30d1\u30c3\u30b1\u30fc\u30b8\u3092\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3059\u308b\u5fc5\u8981\u3082\u3042\u308a\u307e\u3059\u3002\u6163\u4f8b\u3068\u3057\u3066\u3001\u30b3\u30e1\u30f3\u30c8\u306f //Package \u3067\u59cb\u307e\u308a\u3001\u305d\u306e\u5f8c\u306b\u30d1\u30c3\u30b1\u30fc\u30b8\u540d\u304c\u7d9a\u304d\u307e\u3059\u3002\u30d1\u30c3\u30b1\u30fc\u30b8\u30b3\u30e1\u30f3\u30c8\u306e\u6700\u521d\u306e\u884c\u306f\u3001\u30d1\u30c3\u30b1\u30fc\u30b8\u306b\u8868\u793a\u3055\u308c\u308b\u305f\u3081\u7c21\u6f54\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u3057\u3066\u3001\u6b21\u306e\u884c\u306b\u5fc5\u8981\u306a\u60c5\u5831\u3092\u3059\u3079\u3066\u5165\u529b\u3057\u307e\u3059\u3002

\u30b3\u30fc\u30c9\u3092\u6587\u7ae0\u5316\u3059\u308b\u3053\u3068\u304c\u5236\u7d04\u306b\u306a\u308b\u3079\u304d\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3084\u30e1\u30f3\u30c6\u30ca\u304c\u30b3\u30fc\u30c9\u306e\u610f\u56f3\u3092\u7406\u89e3\u3059\u308b\u306e\u306b\u5f79\u7acb\u3064\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

"},{"location":"ja/#16","title":"\u30ea\u30f3\u30bf\u30fc\u3092\u4f7f\u7528\u3057\u3066\u306a\u3044 (#16)","text":"\u8981\u7d04

\u30b3\u30fc\u30c9\u306e\u54c1\u8cea\u3068\u4e00\u8cab\u6027\u3092\u5411\u4e0a\u3055\u305b\u308b\u306b\u306f\u3001\u30ea\u30f3\u30bf\u30fc\u3068\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u30fc\u3092\u4f7f\u7528\u3057\u307e\u3057\u3087\u3046

\u30ea\u30f3\u30bf\u30fc\u306f\u3001\u30b3\u30fc\u30c9\u3092\u5206\u6790\u3057\u3066\u30a8\u30e9\u30fc\u3092\u691c\u51fa\u3059\u308b\u81ea\u52d5\u30c4\u30fc\u30eb\u3067\u3059\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u306e\u76ee\u7684\u306f\u3001\u65e2\u5b58\u306e\u30ea\u30f3\u30bf\u30fc\u306e\u5b8c\u5168\u306a\u30ea\u30b9\u30c8\u3092\u63d0\u4f9b\u3059\u308b\u3053\u3068\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u305d\u3046\u3057\u305f\u5834\u5408\u3001\u3059\u3050\u306b\u4f7f\u3044\u7269\u306b\u306a\u3089\u306a\u304f\u306a\u3063\u3066\u3057\u307e\u3046\u304b\u3089\u3067\u3059\u3002\u305f\u3060\u3057\u3001\u307b\u3068\u3093\u3069\u306e Go \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306b\u30ea\u30f3\u30bf\u30fc\u304c\u4e0d\u53ef\u6b20\u3067\u3042\u308b\u3068\u3044\u3046\u3053\u3068\u306f\u7406\u89e3\u3057\u3001\u899a\u3048\u3066\u304a\u304d\u307e\u3057\u3087\u3046\u3002

  • https://golang.org/cmd/vet\u2015\u2015Go\u8a00\u8a9e\u306e\u6a19\u6e96\u30b3\u30fc\u30c9\u30a2\u30ca\u30e9\u30a4\u30b6\u30fc
  • https://github.com/kisielk/errcheck\u2015\u2015\u30a8\u30e9\u30fc\u30c1\u30a7\u30c3\u30ab\u30fc
  • https://github.com/fzipp/gocyclo\u2015\u2015\u5faa\u74b0\u7684\u8907\u96d1\u5ea6\u30a2\u30ca\u30e9\u30a4\u30b6\u30fc
  • https://github.com/jgautheron/goconst\u2015\u2015\u8907\u6570\u56de\u4f7f\u7528\u6587\u5b57\u5217\u30a2\u30ca\u30e9\u30a4\u30b6\u30fc

\u30ea\u30f3\u30bf\u30fc\u306e\u307b\u304b\u306b\u3001\u30b3\u30fc\u30c9\u30b9\u30bf\u30a4\u30eb\u3092\u4fee\u6b63\u3059\u308b\u305f\u3081\u306b\u30b3\u30fc\u30c9\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u30fc\u3082\u4f7f\u7528\u3057\u307e\u3057\u3087\u3046\u3002\u4ee5\u4e0b\u306b\u3001\u3044\u304f\u3064\u304b\u306e\u30b3\u30fc\u30c9\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u30fc\u3092\u793a\u3057\u307e\u3059\u3002

  • https://golang.org/cmd/gofmt\u2015\u2015Go\u8a00\u8a9e\u306e\u6a19\u6e96\u30b3\u30fc\u30c9\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u30fc
  • https://godoc.org/golang.org/x/tools/cmd/goimports\u2015\u2015Go\u8a00\u8a9e\u306e\u6a19\u6e96\u30a4\u30f3\u30dd\u30fc\u30c8\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u30fc

\u307b\u304b\u306b golangci-lint (https://github.com/golangci/golangci-lint) \u3068\u3044\u3046\u3082\u306e\u304c\u3042\u308a\u307e\u3059\u3002\u3053\u308c\u306f\u3001\u591a\u304f\u306e\u4fbf\u5229\u306a\u30ea\u30f3\u30bf\u30fc\u3084\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u30fc\u306e\u4e0a\u306b\u30d5\u30a1\u30b5\u30fc\u30c9\u3092\u63d0\u4f9b\u3059\u308b\u30ea\u30f3\u30c6\u30a3\u30f3\u30b0\u30c4\u30fc\u30eb\u3067\u3059\u3002\u307e\u305f\u3001\u30ea\u30f3\u30bf\u30fc\u3092\u4e26\u5217\u5b9f\u884c\u3057\u3066\u5206\u6790\u901f\u5ea6\u3092\u5411\u4e0a\u3055\u305b\u308b\u3053\u3068\u304c\u3067\u304d\u3001\u975e\u5e38\u306b\u4fbf\u5229\u3067\u3059\u3002

\u30ea\u30f3\u30bf\u30fc\u3068\u30d5\u30a9\u30fc\u30de\u30c3\u30bf\u30fc\u306f\u3001\u30b3\u30fc\u30c9\u30d9\u30fc\u30b9\u306e\u54c1\u8cea\u3068\u4e00\u8cab\u6027\u3092\u5411\u4e0a\u3055\u305b\u308b\u5f37\u529b\u306a\u65b9\u6cd5\u3067\u3059\u3002\u6642\u9593\u3092\u304b\u3051\u3066\u3069\u308c\u3092\u4f7f\u7528\u3059\u3079\u304d\u304b\u3092\u7406\u89e3\u3057\u3001\u305d\u308c\u3089\u306e\u5b9f\u884c\uff08 CI \u3084 Git \u30d7\u30ea\u30b3\u30df\u30c3\u30c8\u30d5\u30c3\u30af\u306a\u3069\uff09\u3092\u81ea\u52d5\u5316\u3057\u307e\u3057\u3087\u3046\u3002

"},{"location":"ja/#_2","title":"\u30c7\u30fc\u30bf\u578b","text":""},{"location":"ja/#8-17","title":"8 \u9032\u6570\u30ea\u30c6\u30e9\u30eb\u3067\u6df7\u4e71\u3092\u62db\u3044\u3066\u3057\u307e\u3046 (#17)","text":"\u8981\u7d04

\u65e2\u5b58\u306e\u30b3\u30fc\u30c9\u3092\u8aad\u3080\u3068\u304d\u306f\u3001 0 \u3067\u59cb\u307e\u308b\u6574\u6570\u30ea\u30c6\u30e9\u30eb\u304c 8 \u9032\u6570\u3067\u3042\u308b\u3053\u3068\u306b\u7559\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u307e\u305f\u3001\u63a5\u982d\u8f9e 0o \u3092\u4ed8\u3051\u308b\u3053\u3068\u30678\u9032\u6570\u3067\u3042\u308b\u3053\u3068\u3092\u660e\u78ba\u306b\u3057\u3001\u8aad\u307f\u3084\u3059\u3055\u3092\u5411\u4e0a\u3055\u305b\u307e\u3057\u3087\u3046\u3002

8 \u9032\u6570\u306f 0 \u3067\u59cb\u307e\u308a\u307e\u3059\uff08\u305f\u3068\u3048\u3070\u3001010 \u306f 10 \u9032\u6570\u306e 8 \u306b\u76f8\u5f53\u3057\u307e\u3059\uff09\u3002\u53ef\u8aad\u6027\u3092\u5411\u4e0a\u3055\u305b\u3001\u5c06\u6765\u306e\u30b3\u30fc\u30c9\u30ea\u30fc\u30c0\u30fc\u306e\u6f5c\u5728\u7684\u306a\u9593\u9055\u3044\u3092\u56de\u907f\u3059\u308b\u306b\u306f\u3001 0o \u63a5\u982d\u8f9e\u3092\u4f7f\u7528\u3057\u3066 8 \u9032\u6570\u3067\u3042\u308b\u3053\u3068\u3092\u660e\u3089\u304b\u306b\u3057\u307e\u3057\u3087\u3046\uff08\u4f8b: 0o10 \uff09\u3002

\u4ed6\u306e\u6574\u6570\u30ea\u30c6\u30e9\u30eb\u8868\u73fe\u306b\u3082\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002

  • \u30d0\u30a4\u30ca\u30ea - \u63a5\u982d\u8f9e 0b \u3042\u308b\u3044\u306f 0B \u3092\u4f7f\u7528\u3057\u307e\u3059\uff08\u305f\u3068\u3048\u3070\u3001 0b \u306f 10 \u9032\u6570\u306e 4 \u306b\u76f8\u5f53\u3057\u307e\u3059\uff09
  • 16\u9032\u6570 - \u63a5\u982d\u8f9e 0x \u3042\u308b\u3044\u306f 0X \u3092\u4f7f\u7528\u3057\u307e\u3059\uff08\u305f\u3068\u3048\u3070\u3001 0xF \u306f 10 \u9032\u6570\u306e 15 \u306b\u76f8\u5f53\u3057\u307e\u3059\uff09\u3002
  • \u865a\u6570 - \u63a5\u5c3e\u8f9e i \u3092\u4f7f\u7528\u3057\u307e\u3059\uff08\u305f\u3068\u3048\u3070\u3001 3i \uff09

\u8aad\u307f\u3084\u3059\u304f\u3059\u308b\u305f\u3081\u306b\u3001\u533a\u5207\u308a\u6587\u5b57\u3068\u3057\u3066\u30a2\u30f3\u30c0\u30fc\u30b9\u30b3\u30a2\uff08 _ \uff09\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001 10 \u5104\u306f 1_000_000_000 \u306e\u3088\u3046\u306b\u66f8\u304f\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u30a2\u30f3\u30c0\u30fc\u30b9\u30b3\u30a2\u306f 0b)00_00_01 \u306e\u3088\u3046\u306b\u4ed6\u306e\u8868\u73fe\u3068\u4f75\u7528\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#18","title":"\u6574\u6570\u30aa\u30fc\u30d0\u30fc\u30d5\u30ed\u30fc\u3092\u7121\u8996\u3057\u3066\u3044\u308b (#18)","text":"\u8981\u7d04

Go\u8a00\u8a9e\u3067\u306f\u6574\u6570\u306e\u30aa\u30fc\u30d0\u30fc\u30d5\u30ed\u30fc\u3068\u30a2\u30f3\u30c0\u30fc\u30d5\u30ed\u30fc\u304c\u88cf\u5074\u3067\u51e6\u7406\u3055\u308c\u308b\u305f\u3081\u3001\u305d\u308c\u3089\u3092\u30ad\u30e3\u30c3\u30c1\u3059\u308b\u72ec\u81ea\u306e\u95a2\u6570\u3092\u5b9f\u88c5\u3067\u304d\u307e\u3059\u3002

Go\u8a00\u8a9e\u3067\u306f\u3001\u30b3\u30f3\u30d1\u30a4\u30eb\u6642\u306b\u691c\u51fa\u3067\u304d\u308b\u6574\u6570\u30aa\u30fc\u30d0\u30fc\u30d5\u30ed\u30fc\u306b\u3088\u3063\u3066\u30b3\u30f3\u30d1\u30a4\u30eb\u30a8\u30e9\u30fc\u304c\u751f\u6210\u3055\u308c\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u6b21\u306e\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002

var counter int32 = math.MaxInt32 + 1\n
constant 2147483648 overflows int32\n

\u305f\u3060\u3057\u3001\u5b9f\u884c\u6642\u306b\u306f\u3001\u6574\u6570\u306e\u30aa\u30fc\u30d0\u30fc\u30d5\u30ed\u30fc\u307e\u305f\u306f\u30a2\u30f3\u30c0\u30fc\u30d5\u30ed\u30fc\u306f\u767a\u751f\u3057\u307e\u305b\u3093\u3002\u3053\u308c\u306b\u3088\u3063\u3066\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30cb\u30c3\u30af\u304c\u767a\u751f\u3059\u308b\u3053\u3068\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u3053\u306e\u52d5\u4f5c\u306f\u3084\u3063\u304b\u3044\u306a\u30d0\u30b0\uff08\u305f\u3068\u3048\u3070\u3001\u8ca0\u306e\u7d50\u679c\u306b\u3064\u306a\u304c\u308b\u6574\u6570\u306e\u5897\u5206\u3084\u6b63\u306e\u6574\u6570\u306e\u52a0\u7b97\u306a\u3069\uff09\u306b\u3064\u306a\u304c\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u305f\u3081\u3001\u982d\u306b\u5165\u308c\u3066\u304a\u304f\u3053\u3068\u304c\u91cd\u8981\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#19","title":"\u6d6e\u52d5\u5c0f\u6570\u70b9\u3092\u7406\u89e3\u3057\u3066\u3044\u306a\u3044 (#19)","text":"\u8981\u7d04

\u7279\u5b9a\u306e\u30c7\u30eb\u30bf\u5185\u3067\u6d6e\u52d5\u5c0f\u6570\u70b9\u6bd4\u8f03\u3092\u884c\u3046\u3068\u3001\u30b3\u30fc\u30c9\u306e\u79fb\u690d\u6027\u3092\u78ba\u4fdd\u3067\u304d\u307e\u3059\u3002\u52a0\u7b97\u307e\u305f\u306f\u6e1b\u7b97\u3092\u5b9f\u884c\u3059\u308b\u3068\u304d\u306f\u3001\u7cbe\u5ea6\u3092\u5411\u4e0a\u3055\u305b\u308b\u305f\u3081\u306b\u3001\u540c\u7a0b\u5ea6\u306e\u5927\u304d\u3055\u306e\u6f14\u7b97\u3092\u30b0\u30eb\u30fc\u30d7\u5316\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u307e\u305f\u3001\u4e57\u7b97\u3068\u9664\u7b97\u306f\u52a0\u7b97\u3068\u6e1b\u7b97\u306e\u524d\u306b\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002

Go\u8a00\u8a9e\u306b\u306f\u3001\uff08\u865a\u6570\u3092\u9664\u3044\u305f\u5834\u5408\uff09 float32 \u3068 float64 \u3068\u3044\u3046 2 \u3064\u306e\u6d6e\u52d5\u5c0f\u6570\u70b9\u578b\u304c\u3042\u308a\u307e\u3059\u3002\u6d6e\u52d5\u5c0f\u6570\u70b9\u306e\u6982\u5ff5\u306f\u3001\u5c0f\u6570\u5024\u3092\u8868\u73fe\u3067\u304d\u306a\u3044\u3068\u3044\u3046\u6574\u6570\u306e\u5927\u304d\u306a\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u305f\u3081\u306b\u767a\u660e\u3055\u308c\u307e\u3057\u305f\u3002\u4e88\u60f3\u5916\u306e\u4e8b\u614b\u3092\u907f\u3051\u308b\u305f\u3081\u306b\u3001\u6d6e\u52d5\u5c0f\u6570\u70b9\u6f14\u7b97\u306f\u5b9f\u969b\u306e\u6f14\u7b97\u306e\u8fd1\u4f3c\u3067\u3042\u308b\u3053\u3068\u3092\u77e5\u3063\u3066\u304a\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

\u305d\u306e\u305f\u3081\u306b\u3001\u4e57\u7b97\u306e\u4f8b\u3092\u898b\u3066\u307f\u307e\u3057\u3087\u3046\u3002

var n float32 = 1.0001\nfmt.Println(n * n)\n

\u3053\u306e\u30b3\u30fc\u30c9\u306b\u304a\u3044\u3066\u306f 1.0001 * 1.0001 = 1.00020001 \u3068\u3044\u3046\u7d50\u679c\u304c\u51fa\u529b\u3055\u308c\u308b\u3053\u3068\u3092\u671f\u5f85\u3059\u308b\u3068\u601d\u3044\u307e\u3059\u3002\u3057\u304b\u3057\u306a\u304c\u3089\u3001\u307b\u3068\u3093\u3069\u306e x86 \u30d7\u30ed\u30bb\u30c3\u30b5\u3067\u306f\u3001\u4ee3\u308f\u308a\u306b 1.0002 \u304c\u51fa\u529b\u3055\u308c\u307e\u3059\u3002

Go\u8a00\u8a9e\u306e float32 \u304a\u3088\u3073 float64 \u578b\u306f\u8fd1\u4f3c\u5024\u3067\u3042\u308b\u305f\u3081\u3001\u3044\u304f\u3064\u304b\u306e\u30eb\u30fc\u30eb\u3092\u5ff5\u982d\u306b\u7f6e\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

  • 2 \u3064\u306e\u6d6e\u52d5\u5c0f\u6570\u70b9\u6570\u3092\u6bd4\u8f03\u3059\u308b\u5834\u5408\u306f\u3001\u305d\u306e\u5dee\u304c\u8a31\u5bb9\u7bc4\u56f2\u5185\u3067\u3042\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3059\u308b\u3002
  • \u52a0\u7b97\u307e\u305f\u306f\u6e1b\u7b97\u3092\u5b9f\u884c\u3059\u308b\u5834\u5408\u3001\u7cbe\u5ea6\u3092\u9ad8\u3081\u308b\u305f\u3081\u306b\u3001\u540c\u3058\u6841\u6570\u306e\u6f14\u7b97\u3092\u30b0\u30eb\u30fc\u30d7\u5316\u3059\u308b\u3002
  • \u7cbe\u5ea6\u3092\u9ad8\u3081\u308b\u305f\u3081\u3001\u4e00\u9023\u306e\u6f14\u7b97\u3067\u52a0\u7b97\u3001\u6e1b\u7b97\u3001\u4e57\u7b97\u3001\u9664\u7b97\u304c\u5fc5\u8981\u306a\u5834\u5408\u306f\u3001\u4e57\u7b97\u3068\u9664\u7b97\u3092\u6700\u521d\u306b\u5b9f\u884c\u3059\u308b\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#20","title":"\u30b9\u30e9\u30a4\u30b9\u306e\u9577\u3055\u3068\u5bb9\u91cf\u3092\u7406\u89e3\u3057\u3066\u3044\u306a\u3044 (#20)","text":"\u8981\u7d04

Go \u958b\u767a\u8005\u306a\u3089\u3070\u3001\u30b9\u30e9\u30a4\u30b9\u306e\u9577\u3055\u3068\u5bb9\u91cf\u306e\u9055\u3044\u3092\u7406\u89e3\u3059\u308b\u3079\u304d\u3067\u3059\u3002\u30b9\u30e9\u30a4\u30b9\u306e\u9577\u3055\u306f\u30b9\u30e9\u30a4\u30b9\u5185\u306e\u4f7f\u7528\u53ef\u80fd\u306a\u8981\u7d20\u306e\u6570\u3067\u3042\u308a\u3001\u30b9\u30e9\u30a4\u30b9\u306e\u5bb9\u91cf\u306f\u30d0\u30c3\u30ad\u30f3\u30b0\u914d\u5217\u5185\u306e\u8981\u7d20\u306e\u6570\u3067\u3059\u3002

\u30bb\u30af\u30b7\u30e7\u30f3\u5168\u6587\u306f\u3053\u3061\u3089\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#21","title":"\u975e\u52b9\u7387\u306a\u30b9\u30e9\u30a4\u30b9\u306e\u521d\u671f\u5316 (#21)","text":"\u8981\u7d04

\u30b9\u30e9\u30a4\u30b9\u3092\u4f5c\u6210\u3059\u308b\u3068\u304d\u3001\u9577\u3055\u304c\u3059\u3067\u306b\u308f\u304b\u3063\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u6307\u5b9a\u3055\u308c\u305f\u9577\u3055\u307e\u305f\u306f\u5bb9\u91cf\u3067\u30b9\u30e9\u30a4\u30b9\u3092\u521d\u671f\u5316\u3057\u307e\u3057\u3087\u3046\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u5272\u308a\u5f53\u3066\u306e\u6570\u304c\u6e1b\u308a\u3001\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u304c\u5411\u4e0a\u3057\u307e\u3059\u3002

make \u3092\u4f7f\u7528\u3057\u3066\u30b9\u30e9\u30a4\u30b9\u3092\u521d\u671f\u5316\u3059\u308b\u3068\u304d\u306b\u3001\u9577\u3055\u3068\u30aa\u30d7\u30b7\u30e7\u30f3\u306e\u5bb9\u91cf\u3092\u6307\u5b9a\u3067\u304d\u307e\u3059\u3002\u3053\u308c\u3089\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u306e\u4e21\u65b9\u306b\u9069\u5207\u306a\u5024\u3092\u6e21\u3059\u3053\u3068\u304c\u9069\u5f53\u3067\u3042\u308b\u306b\u3082\u304b\u304b\u308f\u3089\u305a\u3001\u305d\u308c\u3092\u5fd8\u308c\u308b\u306e\u306f\u3088\u304f\u3042\u308b\u9593\u9055\u3044\u3067\u3059\u3002\u5b9f\u969b\u3001\u8907\u6570\u306e\u30b3\u30d4\u30fc\u304c\u5fc5\u8981\u306b\u306a\u308a\u3001\u4e00\u6642\u7684\u306a\u30d0\u30c3\u30ad\u30f3\u30b0\u914d\u5217\u3092\u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7\u3059\u308b\u305f\u3081\u306b GC \u306b\u8ffd\u52a0\u306e\u52b4\u529b\u304c\u304b\u304b\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306e\u89b3\u70b9\u304b\u3089\u8a00\u3048\u3070\u3001Go \u30e9\u30f3\u30bf\u30a4\u30e0\u306b\u624b\u3092\u5dee\u3057\u4f38\u3079\u306a\u3044\u7406\u7531\u306f\u3042\u308a\u307e\u305b\u3093\u3002

\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u3001\u6307\u5b9a\u3055\u308c\u305f\u5bb9\u91cf\u307e\u305f\u306f\u6307\u5b9a\u3055\u308c\u305f\u9577\u3055\u306e\u30b9\u30e9\u30a4\u30b9\u3092\u5272\u308a\u5f53\u3066\u308b\u3053\u3068\u3067\u3059\u3002 \u3053\u308c\u3089 2 \u3064\u306e\u89e3\u6c7a\u7b56\u306e\u3046\u3061\u30012 \u756a\u76ee\u306e\u89e3\u6c7a\u7b56\u306e\u65b9\u304c\u308f\u305a\u304b\u306b\u9ad8\u901f\u3067\u3042\u308b\u50be\u5411\u304c\u3042\u308b\u3053\u3068\u304c\u308f\u304b\u308a\u307e\u3057\u305f\u3002\u305f\u3060\u3057\u3001\u7279\u5b9a\u306e\u5bb9\u91cf\u3068\u8ffd\u52a0\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u5834\u5408\u306b\u3088\u3063\u3066\u306f\u5b9f\u88c5\u3068\u8aad\u307f\u53d6\u308a\u304c\u5bb9\u6613\u306b\u306a\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#nil-22","title":"nil \u3068\u7a7a\u306e\u30b9\u30e9\u30a4\u30b9\u3092\u6df7\u540c\u3057\u3066\u3044\u308b (#22)","text":"\u8981\u7d04

encoding/json \u3084 reflect \u30d1\u30c3\u30b1\u30fc\u30b8\u306a\u3069\u3092\u4f7f\u7528\u3059\u308b\u3068\u304d\u306b\u3088\u304f\u3042\u308b\u6df7\u4e71\u3092\u907f\u3051\u308b\u305f\u3081\u306b\u306f\u3001nil \u30b9\u30e9\u30a4\u30b9\u3068\u7a7a\u306e\u30b9\u30e9\u30a4\u30b9\u306e\u9055\u3044\u3092\u7406\u89e3\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3069\u3061\u3089\u3082\u9577\u3055\u30bc\u30ed\u3001\u5bb9\u91cf\u30bc\u30ed\u306e\u30b9\u30e9\u30a4\u30b9\u3067\u3059\u304c\u3001\u5272\u308a\u5f53\u3066\u3092\u5fc5\u8981\u3068\u3057\u306a\u3044\u306e\u306f nil \u30b9\u30e9\u30a4\u30b9\u3060\u3051\u3067\u3059\u3002

Go\u8a00\u8a9e\u3067\u306f\u3001nil \u3068\u7a7a\u306e\u30b9\u30e9\u30a4\u30b9\u306f\u533a\u5225\u3055\u308c\u307e\u3059\u3002nil \u30b9\u30e9\u30a4\u30b9\u306f nil \u306b\u7b49\u3057\u3044\u306e\u306b\u5bfe\u3057\u3001\u7a7a\u306e\u30b9\u30e9\u30a4\u30b9\u306e\u9577\u3055\u306f\u30bc\u30ed\u3067\u3059\u3002nil \u30b9\u30e9\u30a4\u30b9\u306f\u7a7a\u3067\u3059\u304c\u3001\u7a7a\u306e\u30b9\u30e9\u30a4\u30b9\u306f\u5fc5\u305a\u3057\u3082nil \u3067\u3042\u308b\u3068\u306f\u9650\u308a\u307e\u305b\u3093\u3002\u4e00\u65b9\u3001nil \u30b9\u30e9\u30a4\u30b9\u306b\u306f\u5272\u308a\u5f53\u3066\u306f\u5fc5\u8981\u3042\u308a\u307e\u305b\u3093\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u5168\u4f53\u3092\u901a\u3057\u3066\u3001\u4ee5\u4e0b\u306e\u65b9\u6cd5\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u306b\u3088\u3063\u3066\u3001\u72b6\u6cc1\u306b\u5fdc\u3058\u3066\u30b9\u30e9\u30a4\u30b9\u3092\u521d\u671f\u5316\u3059\u308b\u3053\u3068\u3092\u898b\u3066\u304d\u307e\u3057\u305f\u3002

  • \u6700\u7d42\u7684\u306a\u9577\u3055\u304c\u4e0d\u660e\u3067\u30b9\u30e9\u30a4\u30b9\u304c\u7a7a\u306e\u5834\u5408\u306f var s []string
  • nil \u3068\u7a7a\u306e\u30b9\u30e9\u30a4\u30b9\u3092\u4f5c\u6210\u3059\u308b\u7cd6\u8863\u69cb\u6587\u3068\u3057\u3066\u306e []string(nil)
  • \u5c06\u6765\u306e\u9577\u3055\u304c\u308f\u304b\u3063\u3066\u3044\u308b\u5834\u5408\u306f make([]string, length)

\u8981\u7d20\u306a\u3057\u3067\u30b9\u30e9\u30a4\u30b9\u3092\u521d\u671f\u5316\u3059\u308b\u5834\u5408\u3001\u6700\u5f8c\u306e\u30aa\u30d7\u30b7\u30e7\u30f3 []string{} \u306f\u907f\u3051\u308b\u3079\u304d\u3067\u3059\u3002\u6700\u5f8c\u306b\u3001\u4e88\u60f3\u5916\u306e\u52d5\u4f5c\u3092\u9632\u3050\u305f\u3081\u306b\u3001\u4f7f\u7528\u3059\u308b\u30e9\u30a4\u30d6\u30e9\u30ea\u304c nil \u3068\u7a7a\u306e\u30b9\u30e9\u30a4\u30b9\u3092\u533a\u5225\u3057\u3066\u3044\u308b\u304b\u3069\u3046\u304b\u3092\u78ba\u8a8d\u3057\u3066\u307f\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#23","title":"\u30b9\u30e9\u30a4\u30b9\u304c\u7a7a\u304b\u3069\u3046\u304b\u3092\u9069\u5207\u306b\u78ba\u8a8d\u3057\u306a\u3044 (#23)","text":"\u8981\u7d04

\u30b9\u30e9\u30a4\u30b9\u306b\u8981\u7d20\u304c\u542b\u307e\u308c\u3066\u3044\u306a\u3044\u3053\u3068\u3092\u78ba\u8a8d\u3059\u308b\u306b\u306f\u3001\u305d\u306e\u9577\u3055\u3092\u78ba\u8a8d\u3057\u307e\u3057\u3087\u3046\u3002\u3053\u308c\u306f\u3001\u30b9\u30e9\u30a4\u30b9\u304c nil \u3067\u3042\u308b\u304b\u7a7a\u3067\u3042\u308b\u304b\u306b\u95a2\u4fc2\u306a\u304f\u6a5f\u80fd\u3057\u307e\u3059\u3002\u30de\u30c3\u30d7\u306b\u3064\u3044\u3066\u3082\u540c\u69d8\u3067\u3059\u3002\u660e\u78ba\u306a API \u3092\u8a2d\u8a08\u3059\u308b\u306b\u306f\u3001nil \u30b9\u30e9\u30a4\u30b9\u3068\u7a7a\u306e\u30b9\u30e9\u30a4\u30b9\u3092\u533a\u5225\u3057\u306a\u3044\u3067\u304f\u3060\u3055\u3044\u3002

\u30b9\u30e9\u30a4\u30b9\u306b\u8981\u7d20\u304c\u3042\u308b\u304b\u3069\u3046\u304b\u3092\u5224\u65ad\u3059\u308b\u306b\u306f\u3001\u30b9\u30e9\u30a4\u30b9\u304c nil \u304b\u3069\u3046\u304b\u3001\u307e\u305f\u306f\u305d\u306e\u9577\u3055\u304c 0 \u306b\u7b49\u3057\u3044\u304b\u3069\u3046\u304b\u3092\u78ba\u8a8d\u3059\u308b\u3053\u3068\u3067\u5224\u65ad\u3067\u304d\u307e\u3059\u3002\u30b9\u30e9\u30a4\u30b9\u304c\u7a7a\u3067\u3042\u308b\u5834\u5408\u3068\u30b9\u30e9\u30a4\u30b9\u304c nil \u3067\u3042\u308b\u5834\u5408\u306e\u4e21\u65b9\u3092\u30ab\u30d0\u30fc\u3067\u304d\u308b\u305f\u3081\u3001\u9577\u3055\u3092\u78ba\u304b\u3081\u308b\u3053\u3068\u304c\u6700\u826f\u306e\u65b9\u6cd5\u3067\u3059\u3002

\u4e00\u65b9\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u8a2d\u8a08\u3059\u308b\u3068\u304d\u306f\u3001\u8efd\u5fae\u306a\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3055\u306a\u3044\u3088\u3046 nil \u30b9\u30e9\u30a4\u30b9\u3068\u7a7a\u306e\u30b9\u30e9\u30a4\u30b9\u3092\u533a\u5225\u3057\u306a\u3044\u3088\u3046\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u30b9\u30e9\u30a4\u30b9\u3092\u8fd4\u3059\u3068\u304d\u306b\u3001nil \u307e\u305f\u306f\u7a7a\u306e\u30b9\u30e9\u30a4\u30b9\u3092\u8fd4\u3059\u304b\u3069\u3046\u304b\u306f\u3001\u610f\u5473\u7684\u306b\u3082\u6280\u8853\u7684\u306b\u3082\u9055\u3044\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u30b3\u30fc\u30e9\u30fc\u306b\u3068\u3063\u3066\u306f\u3069\u3061\u3089\u3082\u540c\u3058\u3053\u3068\u3092\u610f\u5473\u3059\u308b\u306f\u305a\u3067\u3059\u3002\u3053\u306e\u539f\u7406\u306f\u30de\u30c3\u30d7\u3067\u3082\u540c\u3058\u3067\u3059\u3002\u30de\u30c3\u30d7\u304c\u7a7a\u304b\u3069\u3046\u304b\u3092\u78ba\u8a8d\u3059\u308b\u306b\u306f\u3001\u305d\u308c\u304c nil \u304b\u3069\u3046\u304b\u3067\u306f\u306a\u304f\u3001\u305d\u306e\u9577\u3055\u3092\u78ba\u8a8d\u3057\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#24","title":"\u30b9\u30e9\u30a4\u30b9\u306e\u30b3\u30d4\u30fc\u3092\u6b63\u3057\u304f\u4f5c\u6210\u3057\u3066\u3044\u306a\u3044 (#24)","text":"\u8981\u7d04

\u7d44\u307f\u8fbc\u307f\u95a2\u6570 copy \u3092\u4f7f\u7528\u3057\u3066\u3042\u308b\u30b9\u30e9\u30a4\u30b9\u3092\u5225\u306e\u30b9\u30e9\u30a4\u30b9\u306b\u30b3\u30d4\u30fc\u3059\u308b\u306b\u306f\u3001\u30b3\u30d4\u30fc\u3055\u308c\u308b\u8981\u7d20\u306e\u6570\u304c 2 \u3064\u306e\u30b9\u30e9\u30a4\u30b9\u306e\u9577\u3055\u306e\u9593\u306e\u6700\u5c0f\u5024\u306b\u76f8\u5f53\u3059\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u8981\u7d20\u3092\u3042\u308b\u30b9\u30e9\u30a4\u30b9\u304b\u3089\u5225\u306e\u30b9\u30e9\u30a4\u30b9\u306b\u30b3\u30d4\u30fc\u3059\u308b\u64cd\u4f5c\u306f\u3001\u304b\u306a\u308a\u983b\u7e41\u306b\u884c\u308f\u308c\u307e\u3059\u3002\u30b3\u30d4\u30fc\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u3001\u30b3\u30d4\u30fc\u5148\u306b\u30b3\u30d4\u30fc\u3055\u308c\u308b\u8981\u7d20\u306e\u6570\u306f 2 \u3064\u306e\u30b9\u30e9\u30a4\u30b9\u306e\u9577\u3055\u306e\u9593\u306e\u6700\u5c0f\u5024\u306b\u76f8\u5f53\u3059\u308b\u3053\u3068\u306b\u6ce8\u610f\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u307e\u305f\u3001\u30b9\u30e9\u30a4\u30b9\u3092\u30b3\u30d4\u30fc\u3059\u308b\u305f\u3081\u306e\u4ed6\u306e\u4ee3\u66ff\u624b\u6bb5\u304c\u5b58\u5728\u3059\u308b\u3053\u3068\u306b\u3082\u7559\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u305d\u306e\u305f\u3081\u3001\u30b3\u30fc\u30c9\u30d9\u30fc\u30b9\u3067\u305d\u308c\u3089\u3092\u898b\u3064\u3051\u3066\u3082\u9a5a\u304f\u3053\u3068\u306f\u3042\u308a\u307e\u305b\u3093\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#append-25","title":"append \u306e\u4f7f\u7528\u306b\u3088\u308b\u4e88\u60f3\u5916\u306e\u526f\u4f5c\u7528 (#25)","text":"\u8981\u7d04

2\u3064\u306e\u7570\u306a\u308b\u95a2\u6570\u304c\u540c\u3058\u914d\u5217\u306b\u57fa\u3065\u304f\u30b9\u30e9\u30a4\u30b9\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u306b\u3001copy \u307e\u305f\u306f\u5b8c\u5168\u30b9\u30e9\u30a4\u30b9\u5f0f\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067 append \u306b\u3088\u308b\u885d\u7a81\u3092\u9632\u3050\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u5927\u304d\u306a\u30b9\u30e9\u30a4\u30b9\u3092\u7e2e\u5c0f\u3059\u308b\u5834\u5408\u3001\u30e1\u30e2\u30ea\u30ea\u30fc\u30af\u3092\u9632\u3050\u3053\u3068\u304c\u3067\u304d\u308b\u306e\u306f\u30b9\u30e9\u30a4\u30b9\u306e\u30b3\u30d4\u30fc\u3060\u3051\u3067\u3059\u3002

\u30b9\u30e9\u30a4\u30b9\u3092\u4f7f\u7528\u3059\u308b\u3068\u304d\u306f\u3001\u4e88\u60f3\u5916\u306e\u526f\u4f5c\u7528\u306b\u3064\u306a\u304c\u308b\u72b6\u6cc1\u306b\u76f4\u9762\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u3053\u3068\u3092\u899a\u3048\u3066\u304a\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u7d50\u679c\u306e\u30b9\u30e9\u30a4\u30b9\u306e\u9577\u3055\u304c\u305d\u306e\u5bb9\u91cf\u3088\u308a\u5c0f\u3055\u3044\u5834\u5408\u3001\u8ffd\u52a0\u306b\u3088\u3063\u3066\u5143\u306e\u30b9\u30e9\u30a4\u30b9\u304c\u5909\u66f4\u3055\u308c\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u8d77\u3053\u308a\u5f97\u308b\u526f\u4f5c\u7528\u306e\u7bc4\u56f2\u3092\u5236\u9650\u3057\u305f\u3044\u5834\u5408\u306f\u3001\u30b9\u30e9\u30a4\u30b9\u306e\u30b3\u30d4\u30fc\u307e\u305f\u306f\u5b8c\u5168\u30b9\u30e9\u30a4\u30b9\u5f0f\u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u30b3\u30d4\u30fc\u3092\u5b9f\u884c\u3067\u304d\u306a\u304f\u306a\u308a\u307e\u3059\u3002

\u88dc\u8db3

s[low:high:max]\uff08\u5b8c\u5168\u30b9\u30e9\u30a4\u30b9\u5f0f\uff09\u2015\u2015\u3053\u306e\u547d\u4ee4\u6587\u306f\u3001\u5bb9\u91cf\u304c max - low \u306b\u7b49\u3057\u3044\u3053\u3068\u3092\u9664\u3051\u3070\u3001s[low:high] \u3067\u4f5c\u6210\u3055\u308c\u305f\u30b9\u30e9\u30a4\u30b9\u3068\u540c\u69d8\u306e\u30b9\u30e9\u30a4\u30b9\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#26","title":"\u30b9\u30e9\u30a4\u30b9\u3068\u30e1\u30e2\u30ea\u30ea\u30fc\u30af (#26)","text":"\u8981\u7d04

\u30dd\u30a4\u30f3\u30bf\u306e\u30b9\u30e9\u30a4\u30b9\u307e\u305f\u306f\u30dd\u30a4\u30f3\u30bf\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u6301\u3064\u69cb\u9020\u4f53\u3092\u64cd\u4f5c\u3059\u308b\u5834\u5408\u3001\u30b9\u30e9\u30a4\u30b9\u64cd\u4f5c\u306b\u3088\u3063\u3066\u9664\u5916\u3055\u308c\u305f\u8981\u7d20\u3092 nil \u3068\u3059\u308b\u3053\u3068\u3067\u30e1\u30e2\u30ea\u30ea\u30fc\u30af\u3092\u56de\u907f\u3067\u304d\u307e\u3059\u3002

"},{"location":"ja/#_3","title":"\u5bb9\u91cf\u6f0f\u308c","text":"

\u5927\u304d\u306a\u30b9\u30e9\u30a4\u30b9\u307e\u305f\u306f\u914d\u5217\u3092\u30b9\u30e9\u30a4\u30b9\u3059\u308b\u3068\u3001\u30e1\u30e2\u30ea\u6d88\u8cbb\u304c\u9ad8\u304f\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u6b8b\u308a\u306e\u30b9\u30da\u30fc\u30b9\u306f GC \u306b\u3088\u3063\u3066\u518d\u5229\u7528\u3055\u308c\u305a\u3001\u5c11\u6570\u306e\u8981\u7d20\u3057\u304b\u4f7f\u7528\u3057\u306a\u3044\u306b\u3082\u304b\u304b\u308f\u3089\u305a\u3001\u5927\u304d\u306a\u30d0\u30c3\u30ad\u30f3\u30b0\u914d\u5217\u304c\u4fdd\u6301\u3055\u308c\u307e\u3059\u3002\u30b9\u30e9\u30a4\u30b9\u306e\u30b3\u30d4\u30fc\u3092\u3059\u308b\u3053\u3068\u3067\u3001\u3053\u306e\u3088\u3046\u306a\u4e8b\u614b\u3092\u9632\u3050\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#_4","title":"\u30b9\u30e9\u30a4\u30b9\u3068\u30dd\u30a4\u30f3\u30bf","text":"

\u30dd\u30a4\u30f3\u30bf\u307e\u305f\u306f\u30dd\u30a4\u30f3\u30bf\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u542b\u3080\u69cb\u9020\u4f53\u3092\u4f7f\u7528\u3057\u3066\u30b9\u30e9\u30a4\u30b9\u64cd\u4f5c\u3092\u3059\u308b\u5834\u5408\u3001GC \u304c\u3053\u308c\u3089\u306e\u8981\u7d20\u3092\u518d\u5229\u7528\u3057\u306a\u3044\u3053\u3068\u3092\u77e5\u3063\u3066\u304a\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u306e\u5834\u5408\u306e\u9078\u629e\u80a2\u306f\u3001\u30b3\u30d4\u30fc\u3092\u5b9f\u884c\u3059\u308b\u304b\u3001\u6b8b\u308a\u306e\u8981\u7d20\u307e\u305f\u306f\u305d\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u660e\u793a\u7684\u306b nil \u3068\u3059\u308b\u3053\u3068\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#27","title":"\u975e\u52b9\u7387\u306a\u30de\u30c3\u30d7\u306e\u521d\u671f\u5316 (#27)","text":"\u8981\u7d04

\u30de\u30c3\u30d7\u3092\u4f5c\u6210\u3059\u308b\u3068\u304d\u3001\u305d\u306e\u9577\u3055\u304c\u3059\u3067\u306b\u308f\u304b\u3063\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u6307\u5b9a\u3055\u308c\u305f\u9577\u3055\u3067\u521d\u671f\u5316\u3057\u307e\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u5272\u308a\u5f53\u3066\u306e\u6570\u304c\u6e1b\u308a\u3001\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u304c\u5411\u4e0a\u3057\u307e\u3059\u3002

\u30de\u30c3\u30d7\u306f\u3001\u30ad\u30fc\u30fb\u5024\u30da\u30a2\u306e\u9806\u5e8f\u306a\u3057\u30b3\u30ec\u30af\u30b7\u30e7\u30f3\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002\u306a\u304a\u3001\u305d\u308c\u305e\u308c\u306e\u30da\u30a2\u306f\u56fa\u6709\u306e\u30ad\u30fc\u3092\u6301\u3061\u307e\u3059\u3002Go\u8a00\u8a9e\u3067\u306f\u3001\u30de\u30c3\u30d7\u306f\u30cf\u30c3\u30b7\u30e5\u30c6\u30fc\u30d6\u30eb\u30c7\u30fc\u30bf\u69cb\u9020\u306b\u57fa\u3065\u3044\u3066\u3044\u307e\u3059\u3002\u5185\u90e8\u7684\u306b\u306f\u3001\u30cf\u30c3\u30b7\u30e5\u30c6\u30fc\u30d6\u30eb\u306f\u30d0\u30b1\u30c3\u30c8\u306e\u914d\u5217\u3067\u3042\u308a\u3001\u5404\u30d0\u30b1\u30c3\u30c8\u306f\u30ad\u30fc\u30fb\u5024\u30da\u30a2\u306e\u914d\u5217\u3078\u306e\u30dd\u30a4\u30f3\u30bf\u3067\u3059\u3002

\u30de\u30c3\u30d7\u306b\u542b\u307e\u308c\u308b\u8981\u7d20\u306e\u6570\u304c\u4e8b\u524d\u306b\u308f\u304b\u3063\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u305d\u306e\u521d\u671f\u30b5\u30a4\u30ba\u3092\u6307\u5b9a\u3057\u3066\u4f5c\u6210\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u30de\u30c3\u30d7\u306e\u5897\u5927\u306f\u3001\u5341\u5206\u306a\u30b9\u30da\u30fc\u30b9\u3092\u518d\u5272\u308a\u5f53\u3066\u3057\u3001\u3059\u3079\u3066\u306e\u8981\u7d20\u306e\u30d0\u30e9\u30f3\u30b9\u3092\u518d\u8abf\u6574\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u305f\u3081\u3001\u8a08\u7b97\u91cf\u304c\u975e\u5e38\u306b\u591a\u304f\u306a\u308a\u307e\u3059\u304c\u3001\u3053\u308c\u306b\u3088\u308a\u305d\u308c\u3092\u56de\u907f\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#28","title":"\u30de\u30c3\u30d7\u3068\u30e1\u30e2\u30ea\u30ea\u30fc\u30af (#28)","text":"\u8981\u7d04

\u30de\u30c3\u30d7\u306f\u30e1\u30e2\u30ea\u5185\u3067\u5e38\u306b\u5897\u5927\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u304c\u3001\u7e2e\u5c0f\u3059\u308b\u3053\u3068\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u30e1\u30e2\u30ea\u306e\u554f\u984c\u304c\u767a\u751f\u3059\u308b\u5834\u5408\u306f\u3001\u30de\u30c3\u30d7\u3092\u5f37\u5236\u7684\u306b\u518d\u751f\u6210\u3057\u305f\u308a\u3001\u30dd\u30a4\u30f3\u30bf\u3092\u4f7f\u7528\u3057\u305f\u308a\u3059\u308b\u306a\u3069\u3001\u3055\u307e\u3056\u307e\u306a\u624b\u6bb5\u3092\u8a66\u3059\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

\u30bb\u30af\u30b7\u30e7\u30f3\u5168\u6587\u306f\u3053\u3061\u3089\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#29","title":"\u8aa4\u3063\u305f\u65b9\u6cd5\u306b\u3088\u308b\u5024\u306e\u6bd4\u8f03 (#29)","text":"\u8981\u7d04

Go\u8a00\u8a9e\u3067\u578b\u3092\u6bd4\u8f03\u3059\u200b\u200b\u308b\u306b\u306f\u30012 \u3064\u306e\u578b\u304c\u6bd4\u8f03\u53ef\u80fd\u306a\u3089\u3070\u3001== \u6f14\u7b97\u5b50\u3068 != \u6f14\u7b97\u5b50\u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002\u771f\u507d\u5024\u3001\u6570\u5024\u3001\u6587\u5b57\u5217\u3001\u30dd\u30a4\u30f3\u30bf\u3001\u30c1\u30e3\u30cd\u30eb\u3001\u304a\u3088\u3073\u69cb\u9020\u4f53\u304c\u5b8c\u5168\u306b\u6bd4\u8f03\u53ef\u80fd\u306a\u578b\u3067\u69cb\u6210\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u305d\u308c\u4ee5\u5916\u306f\u3001 reflect.DeepEqual \u3092\u4f7f\u7528\u3057\u3066\u30ea\u30d5\u30ec\u30af\u30b7\u30e7\u30f3\u306e\u4ee3\u511f\u3092\u652f\u6255\u3046\u304b\u3001\u72ec\u81ea\u306e\u5b9f\u88c5\u3068\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

\u52b9\u679c\u7684\u306b\u6bd4\u8f03\u3059\u308b\u306b\u306f\u3001 == \u3068 != \u306e\u4f7f\u7528\u65b9\u6cd5\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u304c\u4e0d\u53ef\u6b20\u3067\u3059\u3002\u3053\u308c\u3089\u306e\u6f14\u7b97\u5b50\u306f\u3001\u6bd4\u8f03\u53ef\u80fd\u306a\u88ab\u6f14\u7b97\u5b50\u3067\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002

  • \u771f\u507d\u5024 - 2 \u3064\u306e\u771f\u507d\u5024\u304c\u7b49\u3057\u3044\u304b\u3069\u3046\u304b\u3092\u6bd4\u8f03\u3057\u307e\u3059\u3002
  • \u6570\u5024 (int\u3001float\u3001\u304a\u3088\u3073 complex \u578b) - 2 \u3064\u306e\u6570\u5024\u304c\u7b49\u3057\u3044\u304b\u3069\u3046\u304b\u3092\u6bd4\u8f03\u3057\u307e\u3059\u3002
  • \u6587\u5b57\u5217 - 2 \u3064\u306e\u6587\u5b57\u5217\u304c\u7b49\u3057\u3044\u304b\u3069\u3046\u304b\u3092\u6bd4\u8f03\u3057\u307e\u3059\u3002
  • \u30c1\u30e3\u30cd\u30eb - 2 \u3064\u306e\u30c1\u30e3\u30cd\u30eb\u304c\u540c\u3058 make \u547c\u3073\u51fa\u3057\u306b\u3088\u3063\u3066\u4f5c\u6210\u3055\u308c\u305f\u304b\u3001\u307e\u305f\u306f\u4e21\u65b9\u304c nil \u3067\u3042\u308b\u304b\u3092\u6bd4\u8f03\u3057\u307e\u3059\u3002
  • \u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9 - 2 \u3064\u306e\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u304c\u540c\u3058\u52d5\u7684\u30bf\u30a4\u30d7\u3068\u7b49\u3057\u3044\u52d5\u7684\u5024\u3092\u6301\u3064\u304b\u3069\u3046\u304b\u3001\u307e\u305f\u306f\u4e21\u65b9\u304c nil \u3067\u3042\u308b\u304b\u3069\u3046\u304b\u3092\u6bd4\u8f03\u3057\u307e\u3059\u3002
  • \u30dd\u30a4\u30f3\u30bf - 2 \u3064\u306e\u30dd\u30a4\u30f3\u30bf\u304c\u30e1\u30e2\u30ea\u5185\u306e\u540c\u3058\u5024\u3092\u6307\u3057\u3066\u3044\u308b\u304b\u3001\u307e\u305f\u306f\u4e21\u65b9\u3068\u3082 nil \u3067\u3042\u308b\u304b\u3092\u6bd4\u8f03\u3057\u307e\u3059\u3002
  • \u69cb\u9020\u4f53\u3068\u914d\u5217 - \u985e\u4f3c\u3057\u305f\u578b\u3067\u69cb\u6210\u3055\u308c\u3066\u3044\u308b\u304b\u3069\u3046\u304b\u3092\u6bd4\u8f03\u3057\u307e\u3059\u3002
\u88dc\u8db3

? \u3001 >= \u3001 < \u3001\u304a\u3088\u3073 > \u6f14\u7b97\u5b50\u3092\u6570\u5024\u578b\u3067\u4f7f\u7528\u3057\u3066\u5024\u3092\u6bd4\u8f03\u3057\u305f\u308a\u3001\u6587\u5b57\u5217\u3067\u5b57\u53e5\u9806\u5e8f\u3092\u6bd4\u8f03\u3057\u305f\u308a\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002

\u88ab\u6f14\u7b97\u5b50\u304c\u6bd4\u8f03\u3067\u304d\u306a\u3044\u5834\u5408\uff08\u30b9\u30e9\u30a4\u30b9\u3068\u30de\u30c3\u30d7\u306a\u3069\uff09\u3001\u30ea\u30d5\u30ec\u30af\u30b7\u30e7\u30f3\u306a\u3069\u306e\u4ed6\u306e\u65b9\u6cd5\u3092\u5229\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u30ea\u30d5\u30ec\u30af\u30b7\u30e7\u30f3\u306f\u30e1\u30bf\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u306e\u4e00\u7a2e\u3067\u3042\u308a\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u305d\u306e\u69cb\u9020\u3068\u52d5\u4f5c\u3092\u5185\u7701\u3057\u3066\u5909\u66f4\u3059\u308b\u6a5f\u80fd\u3092\u6307\u3057\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001Go\u8a00\u8a9e\u3067\u306f reflect.DeepEqual \u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002\u3053\u306e\u95a2\u6570\u306f\u30012\u3064\u306e\u5024\u3092\u518d\u5e30\u7684\u306b\u8abf\u3079\u308b\u3053\u3068\u306b\u3088\u3063\u3066\u30012\u3064\u306e\u8981\u7d20\u304c\u5b8c\u5168\u306b\u7b49\u3057\u3044\u304b\u3069\u3046\u304b\u3092\u5831\u544a\u3057\u307e\u3059\u3002\u53d7\u3051\u5165\u308c\u3089\u308c\u308b\u8981\u7d20\u306f\u3001\u57fa\u672c\u578b\u306b\u52a0\u3048\u3066\u3001\u914d\u5217\u3001\u69cb\u9020\u4f53\u3001\u30b9\u30e9\u30a4\u30b9\u3001\u30de\u30c3\u30d7\u3001\u30dd\u30a4\u30f3\u30bf\u3001\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3001\u95a2\u6570\u3067\u3059\u3002\u3057\u304b\u3057\u3001\u6700\u5927\u306e\u843d\u3068\u3057\u7a74\u306f\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u4e0a\u306e\u30da\u30ca\u30eb\u30c6\u30a3\u3067\u3059\u3002

\u5b9f\u884c\u6642\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u304c\u91cd\u8981\u306a\u5834\u5408\u306f\u3001\u72ec\u81ea\u306e\u30e1\u30bd\u30c3\u30c9\u3092\u5b9f\u88c5\u3059\u308b\u3053\u3068\u304c\u6700\u5584\u3068\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002

\u8ffd\u8a18\uff1a\u6a19\u6e96\u30e9\u30a4\u30d6\u30e9\u30ea\u306b\u306f\u65e2\u306b\u6bd4\u8f03\u30e1\u30bd\u30c3\u30c9\u304c\u3044\u304f\u3064\u304b\u3042\u308b\u3053\u3068\u3092\u899a\u3048\u3066\u304a\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u6700\u9069\u5316\u3055\u308c\u305f bytes.Compare \u95a2\u6570\u3092\u4f7f\u7528\u3057\u3066\u30012\u3064\u306e\u30d0\u30a4\u30c8\u30b9\u30e9\u30a4\u30b9\u3092\u6bd4\u8f03\u3067\u304d\u307e\u3059\u3002\u72ec\u81ea\u306e\u30e1\u30bd\u30c3\u30c9\u3092\u5b9f\u88c5\u3059\u308b\u524d\u306b\u3001\u8eca\u8f2a\u306e\u518d\u767a\u660e\u3092\u3057\u306a\u3044\u3088\u3046\u306b\u3057\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#_5","title":"\u5236\u5fa1\u69cb\u9020","text":""},{"location":"ja/#range-30","title":"\u8981\u7d20\u304c range \u30eb\u30fc\u30d7\u5185\u3067\u30b3\u30d4\u30fc\u3055\u308c\u308b\u3053\u3068\u3092\u77e5\u3089\u306a\u3044 (#30)","text":"\u8981\u7d04

range \u30eb\u30fc\u30d7\u5185\u306e value \u8981\u7d20\u306f\u30b3\u30d4\u30fc\u3067\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u305f\u3068\u3048\u3070\u69cb\u9020\u4f53\u3092\u5909\u66f4\u3059\u308b\u306b\u306f\u3001\u305d\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u4ecb\u3057\u3066\u30a2\u30af\u30bb\u30b9\u3059\u308b\u304b\u3001\u5f93\u6765\u306e for \u30eb\u30fc\u30d7\u3092\u4ecb\u3057\u3066\u30a2\u30af\u30bb\u30b9\u3057\u307e\u3057\u3087\u3046\uff08\u5909\u66f4\u3059\u308b\u8981\u7d20\u307e\u305f\u306f\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u30dd\u30a4\u30f3\u30bf\u3067\u3042\u308b\u5834\u5408\u3092\u9664\u304f\uff09\u3002

range \u30eb\u30fc\u30d7\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u3055\u307e\u3056\u307e\u306a\u30c7\u30fc\u30bf\u69cb\u9020\u306b\u53cd\u5fa9\u51e6\u7406\u3092\u884c\u3046\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

  • \u6587\u5b57\u5217
  • \u914d\u5217
  • \u914d\u5217\u3078\u306e\u30dd\u30a4\u30f3\u30bf
  • \u30b9\u30e9\u30a4\u30b9
  • \u30de\u30c3\u30d7
  • \u53d7\u4fe1\u30c1\u30e3\u30cd\u30eb

\u53e4\u5178\u7684\u306a for \u30eb\u30fc\u30d7\u3068\u6bd4\u8f03\u3059\u308b\u3068\u3001range \u30eb\u30fc\u30d7\u306f\u305d\u306e\u7c21\u6f54\u306a\u69cb\u6587\u306e\u304a\u304b\u3052\u3067\u3001\u3053\u308c\u3089\u306e\u30c7\u30fc\u30bf\u69cb\u9020\u306e\u3059\u3079\u3066\u306e\u8981\u7d20\u306b\u53cd\u5fa9\u51e6\u7406\u3092\u3059\u308b\u306e\u306b\u4fbf\u5229\u3067\u3059\u3002

\u305f\u3060\u3057\u3001range \u30eb\u30fc\u30d7\u5185\u306e\u5024\u8981\u7d20\u306f\u30b3\u30d4\u30fc\u3067\u3042\u308b\u3053\u3068\u3092\u899a\u3048\u3066\u304a\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u5024\u3092\u5909\u66f4\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u69cb\u9020\u4f53\u306e\u5834\u5408\u3001\u5909\u66f4\u3059\u308b\u5024\u307e\u305f\u306f\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u30dd\u30a4\u30f3\u30bf\u3067\u306a\u3044\u9650\u308a\u3001\u8981\u7d20\u81ea\u4f53\u3067\u306f\u306a\u304f\u30b3\u30d4\u30fc\u306e\u307f\u3092\u66f4\u65b0\u3057\u307e\u3059\u3002range \u30eb\u30fc\u30d7\u307e\u305f\u306f\u5f93\u6765\u306e for \u30eb\u30fc\u30d7\u3092\u4f7f\u7528\u3057\u3066\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u7d4c\u7531\u3067\u8981\u7d20\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u3053\u3068\u304c\u63a8\u5968\u3055\u308c\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#range-31","title":"range \u30eb\u30fc\u30d7\uff08\u30c1\u30e3\u30cd\u30eb\u3068\u914d\u5217\uff09\u3067\u306e\u5f15\u6570\u306e\u8a55\u4fa1\u65b9\u6cd5\u3092\u77e5\u3089\u306a\u3044 (#31)","text":"\u8981\u7d04

range \u6f14\u7b97\u5b50\u306b\u6e21\u3055\u308c\u308b\u5f0f\u306f\u30eb\u30fc\u30d7\u306e\u958b\u59cb\u524d\u306b 1 \u56de\u3060\u3051\u8a55\u4fa1\u3055\u308c\u308b\u3053\u3068\u3092\u7406\u89e3\u3059\u308b\u3068\u3001\u30c1\u30e3\u30cd\u30eb\u307e\u305f\u306f\u30b9\u30e9\u30a4\u30b9\u306e\u53cd\u5fa9\u51e6\u7406\u306b\u304a\u3051\u308b\u975e\u52b9\u7387\u306a\u5272\u308a\u5f53\u3066\u306a\u3069\u306e\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044\u3092\u56de\u907f\u3067\u304d\u307e\u3059\u3002

range \u30eb\u30fc\u30d7\u306f\u3001\uff08\u30bf\u30a4\u30d7\u306b\u95a2\u4fc2\u306a\u304f\uff09\u30b3\u30d4\u30fc\u3092\u5b9f\u884c\u3059\u308b\u3053\u3068\u306b\u3088\u308a\u3001\u30eb\u30fc\u30d7\u306e\u958b\u59cb\u524d\u306b\u3001\u6307\u5b9a\u3055\u308c\u305f\u5f0f\u3092 1 \u56de\u3060\u3051\u8a55\u4fa1\u3057\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u8aa4\u3063\u305f\u8981\u7d20\u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u3057\u307e\u3046\u3001\u3068\u3044\u3046\u3088\u3046\u306a\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044\u3092\u907f\u3051\u308b\u305f\u3081\u306b\u3001\u3053\u306e\u52d5\u4f5c\u3092\u899a\u3048\u3066\u304a\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070

a := [3]int{0, 1, 2}\nfor i, v := range a {\n    a[2] = 10\n    if i == 2 {\n        fmt.Println(v)\n    }\n}\n

\u3053\u306e\u30b3\u30fc\u30c9\u306f\u3001\u6700\u5f8c\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092 10 \u306b\u66f4\u65b0\u3057\u307e\u3059\u3002\u3057\u304b\u3057\u3001\u3053\u306e\u30b3\u30fc\u30c9\u3092\u5b9f\u884c\u3059\u308b\u3068\u300110 \u306f\u51fa\u529b\u3055\u308c\u307e\u305b\u3093\u3002 2 \u304c\u51fa\u529b\u3055\u308c\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#range-32","title":"range \u30eb\u30fc\u30d7\u5185\u306b\u304a\u3051\u308b\u30dd\u30a4\u30f3\u30bf\u8981\u7d20\u306e\u4f7f\u7528\u304c\u53ca\u307c\u3059\u5f71\u97ff\u3092\u5206\u304b\u3063\u3066\u3044\u306a\u3044 (#32)","text":"\u8981\u7d04

\u30ed\u30fc\u30ab\u30eb\u5909\u6570\u3092\u4f7f\u7528\u3059\u308b\u304b\u3001\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u4f7f\u7528\u3057\u3066\u8981\u7d20\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u3068\u3001\u30eb\u30fc\u30d7\u5185\u3067\u30dd\u30a4\u30f3\u30bf\u3092\u30b3\u30d4\u30fc\u3059\u308b\u969b\u306e\u9593\u9055\u3044\u3092\u9632\u3050\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

range \u30eb\u30fc\u30d7\u3092\u4f7f\u7528\u3057\u3066\u30c7\u30fc\u30bf\u69cb\u9020\u306b\u53cd\u5fa9\u51e6\u7406\u3092\u65bd\u3059\u5834\u5408\u3001\u3059\u3079\u3066\u306e\u5024\u304c\u5358\u4e00\u306e\u4e00\u610f\u306e\u30a2\u30c9\u30ec\u30b9\u3092\u6301\u3064\u4e00\u610f\u306e\u5909\u6570\u306b\u5272\u308a\u5f53\u3066\u3089\u308c\u308b\u3053\u3068\u3092\u601d\u3044\u51fa\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3086\u3048\u306b\u3001\u5404\u53cd\u5fa9\u51e6\u7406\u4e2d\u306b\u3053\u306e\u5909\u6570\u3092\u53c2\u7167\u3059\u308b\u30dd\u30a4\u30f3\u30bf\u3092\u4fdd\u5b58\u3059\u308b\u3068\u3001\u540c\u3058\u8981\u7d20\u3001\u3064\u307e\u308a\u6700\u65b0\u306e\u8981\u7d20\u3092\u53c2\u7167\u3059\u308b\u540c\u3058\u30dd\u30a4\u30f3\u30bf\u3092\u4fdd\u5b58\u3059\u308b\u3053\u3068\u306b\u306a\u308a\u307e\u3059\u3002\u3053\u306e\u554f\u984c\u306f\u3001\u30eb\u30fc\u30d7\u306e\u30b9\u30b3\u30fc\u30d7\u5185\u306b\u30ed\u30fc\u30ab\u30eb\u5909\u6570\u3092\u5f37\u5236\u7684\u306b\u4f5c\u6210\u3059\u308b\u304b\u3001\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u4ecb\u3057\u3066\u30b9\u30e9\u30a4\u30b9\u8981\u7d20\u3092\u53c2\u7167\u3059\u308b\u30dd\u30a4\u30f3\u30bf\u3092\u4f5c\u6210\u3059\u308b\u3053\u3068\u3067\u89e3\u6c7a\u3067\u304d\u307e\u3059\u3002\u3069\u3061\u3089\u306e\u89e3\u6c7a\u7b56\u3067\u3082\u554f\u984c\u3042\u308a\u307e\u305b\u3093\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#33","title":"\u30de\u30c3\u30d7\u306e\u53cd\u5fa9\u51e6\u7406\u4e2d\u306b\u8aa4\u3063\u305f\u4eee\u5b9a\u3092\u3059\u308b\uff08\u53cd\u5fa9\u51e6\u7406\u4e2d\u306e\u9806\u5e8f\u4ed8\u3051\u3068\u30de\u30c3\u30d7\u306e\u633f\u5165\uff09 (#33)","text":"\u8981\u7d04

\u30de\u30c3\u30d7\u3092\u4f7f\u7528\u3059\u308b\u3068\u304d\u306b\u4e88\u6e2c\u53ef\u80fd\u306a\u51fa\u529b\u3092\u4fdd\u8a3c\u3059\u308b\u306b\u306f\u3001\u30de\u30c3\u30d7\u306e\u30c7\u30fc\u30bf\u69cb\u9020\u304c\u6b21\u306e\u3068\u304a\u308a\u3067\u3042\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002

  • \u30c7\u30fc\u30bf\u3092\u30ad\u30fc\u3067\u4e26\u3079\u66ff\u3048\u307e\u305b\u3093
  • \u633f\u5165\u9806\u5e8f\u306f\u4fdd\u6301\u3055\u308c\u307e\u305b\u3093
  • \u53cd\u5fa9\u51e6\u7406\u9806\u5e8f\u306f\u6c7a\u307e\u3063\u3066\u3044\u307e\u305b\u3093
  • \u3042\u308b\u53cd\u5fa9\u51e6\u7406\u4e2d\u306b\u8ffd\u52a0\u3055\u308c\u305f\u8981\u7d20\u304c\u305d\u306e\u51e6\u7406\u4e2d\u306b\u751f\u6210\u3055\u308c\u308b\u3053\u3068\u3092\u4fdd\u8a3c\u3057\u307e\u305b\u3093

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#break-34","title":"break \u6587\u304c\u3069\u306e\u3088\u3046\u306b\u6a5f\u80fd\u3059\u308b\u304b\u3092\u5206\u304b\u3063\u3066\u3044\u306a\u3044 (#34)","text":"\u8981\u7d04

\u30e9\u30d9\u30eb\u3068 break \u307e\u305f\u306f continue \u306e\u4f75\u7528\u306f\u3001\u7279\u5b9a\u306e\u547d\u4ee4\u6587\u3092\u5f37\u5236\u7684\u306b\u4e2d\u65ad\u3057\u307e\u3059\u3002\u3053\u308c\u306f\u3001\u30eb\u30fc\u30d7\u5185\u306e switch \u307e\u305f\u306f select \u6587\u3067\u5f79\u7acb\u3061\u307e\u3059\u3002

\u901a\u5e38\u3001break \u6587\u306f\u30eb\u30fc\u30d7\u306e\u5b9f\u884c\u3092\u7d42\u4e86\u3059\u308b\u305f\u3081\u306b\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002\u30eb\u30fc\u30d7\u304c switch \u307e\u305f\u306f select \u3068\u7d44\u307f\u5408\u308f\u305b\u3066\u4f7f\u7528\u200b\u200b\u3055\u308c\u308b\u5834\u5408\u3001\u76ee\u7684\u306e\u547d\u4ee4\u6587\u3067\u306f\u306a\u3044\u306e\u306b\u4e2d\u65ad\u3055\u305b\u3066\u3057\u307e\u3046\u3001\u3068\u3044\u3046\u30df\u30b9\u3092\u3059\u308b\u3053\u3068\u304c\u958b\u767a\u8005\u306b\u306f\u3088\u304f\u3042\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070

for i := 0; i < 5; i++ {\n    fmt.Printf(\"%d \", i)\n\n    switch i {\n    default:\n    case 2:\n        break\n    }\n}\n

break \u6587\u306f for \u30eb\u30fc\u30d7\u3092\u7d42\u4e86\u3055\u305b\u308b\u306e\u3067\u306f\u306a\u304f\u3001\u4ee3\u308f\u308a\u306b switch \u6587\u3092\u7d42\u4e86\u3055\u305b\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u3053\u306e\u30b3\u30fc\u30c9\u306f 0 \u304b\u3089 2 \u307e\u3067\u3092\u53cd\u5fa9\u3059\u308b\u4ee3\u308f\u308a\u306b\u30010 \u304b\u3089 4 \u307e\u3067\u3092\u53cd\u5fa9\u3057\u307e\u3059\uff080 1 2 3 4\uff09\u3002

\u899a\u3048\u3066\u304a\u304f\u3079\u304d\u91cd\u8981\u306a\u30eb\u30fc\u30eb\u306e1\u3064\u306f\u3001 break \u6587\u306f\u6700\u3082\u5185\u5074\u306e for \u3001switch \u3001\u307e\u305f\u306f select \u6587\u306e\u5b9f\u884c\u3092\u7d42\u4e86\u3059\u308b\u3068\u3044\u3046\u3053\u3068\u3067\u3059\u3002\u524d\u306e\u4f8b\u3067\u306f\u3001switch \u6587\u3092\u7d42\u4e86\u3057\u307e\u3059\u3002

switch \u6587\u306e\u4ee3\u308f\u308a\u306b\u30eb\u30fc\u30d7\u3092\u4e2d\u65ad\u3059\u308b\u6700\u3082\u6163\u7528\u7684\u306a\u65b9\u6cd5\u306f\u30e9\u30d9\u30eb\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3059\u3002

loop:\n    for i := 0; i < 5; i++ {\n        fmt.Printf(\"%d \", i)\n\n        switch i {\n        default:\n        case 2:\n            break loop\n        }\n    }\n

\u3053\u3053\u3067\u306f\u3001loop \u30e9\u30d9\u30eb\u3092 for \u30eb\u30fc\u30d7\u306b\u95a2\u9023\u4ed8\u3051\u307e\u3059\u3002 \u6b21\u306b\u3001break \u6587\u306b loop \u30e9\u30d9\u30eb\u3092\u6307\u5b9a\u3059\u308b\u306e\u3067\u3001switch \u3067\u306f\u306a\u304f loop \u304c\u4e2d\u65ad\u3055\u308c\u307e\u3059\u3002\u3088\u3063\u3066\u3001\u3053\u306e\u65b0\u3057\u3044\u30d0\u30fc\u30b8\u30e7\u30f3\u306f\u4e88\u60f3\u3069\u304a\u308a 0 1 2 \u3092\u51fa\u529b\u3057\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#defer-35","title":"\u30eb\u30fc\u30d7\u5185\u3067 defer \u3092\u4f7f\u7528\u3059\u308b (#35)","text":"\u8981\u7d04

\u95a2\u6570\u5185\u306e\u30eb\u30fc\u30d7\u30ed\u30b8\u30c3\u30af\u306e\u62bd\u51fa\u306f\u3001\u5404\u53cd\u5fa9\u306e\u6700\u5f8c\u3067\u306e defer \u6587\u306e\u5b9f\u884c\u306b\u3064\u306a\u304c\u308a\u307e\u3059\u3002

defer \u6587\u306f\u3001\u4e0a\u4f4d\u30d6\u30ed\u30c3\u30af\u306e\u95a2\u6570\u304c\u623b\u308b\u307e\u3067\u547c\u3073\u51fa\u3057\u306e\u5b9f\u884c\u3092\u9045\u3089\u305b\u307e\u3059\u3002\u3053\u308c\u306f\u4e3b\u306b\u5b9a\u578b\u30b3\u30fc\u30c9\u3092\u524a\u6e1b\u3059\u308b\u305f\u3081\u306b\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u30ea\u30bd\u30fc\u30b9\u3092\u6700\u7d42\u7684\u306b\u9589\u3058\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306f\u3001defer \u3092\u4f7f\u7528\u3057\u3066\u3001return \u3092\u5b9f\u884c\u3059\u308b\u524d\u306b\u30af\u30ed\u30fc\u30b8\u30e3\u547c\u3073\u51fa\u3057\u3092\u7e70\u308a\u8fd4\u3059\u3053\u3068\u3092\u907f\u3051\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

defer \u3067\u3088\u304f\u3042\u308b\u30df\u30b9\u306e1\u3064\u306f\u3001\u4e0a\u4f4d\u30d6\u30ed\u30c3\u30af \u306e\u95a2\u6570\u304c\u623b\u3063\u305f\u3068\u304d\u306b\u95a2\u6570\u547c\u3073\u51fa\u3057\u304c\u30b9\u30b1\u30b8\u30e5\u30fc\u30eb\u3055\u308c\u308b\u3053\u3068\u3092\u5fd8\u308c\u308b\u3053\u3068\u3067\u3059\u3002\u305f\u3068\u3048\u3070

func readFiles(ch <-chan string) error {\n    for path := range ch {\n        file, err := os.Open(path)\n        if err != nil {\n            return err\n        }\n\n        defer file.Close()\n\n        // \u30d5\u30a1\u30a4\u30eb\u306e\u51e6\u7406\u3092\u3059\u308b\n    }\n    return nil\n}\n

defer \u547c\u3073\u51fa\u3057\u306f\u3001\u5404\u30eb\u30fc\u30d7\u53cd\u5fa9\u4e2d\u3067\u306f\u306a\u304f\u3001readFiles \u95a2\u6570\u304c\u8fd4\u3055\u308c\u305f\u3068\u304d\u306b\u5b9f\u884c\u3055\u308c\u307e\u3059\u3002 readFiles \u304c\u8fd4\u3089\u306a\u3044\u5834\u5408\u3001\u30d5\u30a1\u30a4\u30eb\u8a18\u8ff0\u5b50\u306f\u6c38\u4e45\u306b\u958b\u3044\u305f\u307e\u307e\u306b\u306a\u308a\u3001\u30ea\u30fc\u30af\u304c\u767a\u751f\u3057\u307e\u3059\u3002

\u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u305f\u3081\u306e\u4e00\u822c\u7684\u306a\u624b\u6bb5\u306e1\u3064\u306f\u3001 defer \u306e\u5f8c\u306b\u3001\u5404\u53cd\u5fa9\u4e2d\u306b\u547c\u3073\u51fa\u3055\u308c\u308b\u4e0a\u4f4d\u30d6\u30ed\u30c3\u30af\u306e\u95a2\u6570\u3092\u4f5c\u6210\u3059\u308b\u3053\u3068\u3067\u3059\u3002

func readFiles(ch <-chan string) error {\n    for path := range ch {\n        if err := readFile(path); err != nil {\n            return err\n        }\n    }\n    return nil\n}\n\nfunc readFile(path string) error {\n    file, err := os.Open(path)\n    if err != nil {\n        return err\n    }\n\n    defer file.Close()\n\n    // \u30d5\u30a1\u30a4\u30eb\u306e\u51e6\u7406\u3092\u3059\u308b\n    return nil\n}\n

\u5225\u306e\u89e3\u6c7a\u7b56\u306f\u3001readFile \u95a2\u6570\u3092\u30af\u30ed\u30fc\u30b8\u30e3\u306b\u3059\u308b\u3053\u3068\u3067\u3059\u304c\u3001\u672c\u8cea\u7684\u306b\u306f\u540c\u3058\u3067\u3059\u3002\u5225\u306e\u4e0a\u4f4d\u30d6\u30ed\u30c3\u30af\u306e\u95a2\u6570\u3092\u8ffd\u52a0\u3057\u3066\u3001\u5404\u53cd\u5fa9\u4e2d\u306b defer \u547c\u3073\u51fa\u3057\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#_6","title":"\u6587\u5b57\u5217","text":""},{"location":"ja/#36","title":"\u30eb\u30fc\u30f3\u3092\u7406\u89e3\u3057\u3066\u3044\u306a\u3044 (#36)","text":"\u8981\u7d04

\u30eb\u30fc\u30f3\u304c Unicode \u30b3\u30fc\u30c9\u30dd\u30a4\u30f3\u30c8\u306e\u6982\u5ff5\u306b\u5bfe\u5fdc\u3057\u3001\u8907\u6570\u306e\u30d0\u30a4\u30c8\u3067\u69cb\u6210\u3055\u308c\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u3053\u3068\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u306f\u3001 Go \u958b\u767a\u8005\u304c\u6587\u5b57\u5217\u3092\u6b63\u78ba\u306b\u64cd\u4f5c\u3059\u308b\u305f\u3081\u306b\u4e0d\u53ef\u6b20\u3067\u3059\u3002

Go\u8a00\u8a9e\u3067\u306f\u30eb\u30fc\u30f3\u304c\u3042\u3089\u3086\u308b\u5834\u6240\u306b\u4f7f\u7528\u3055\u308c\u308b\u305f\u3081\u3001\u6b21\u306e\u70b9\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u304c\u91cd\u8981\u3067\u3059\u3002

  • \u6587\u5b57\u30bb\u30c3\u30c8\u306f\u6587\u5b57\u306e\u96c6\u5408\u3067\u3059\u304c\u3001\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u306f\u6587\u5b57\u30bb\u30c3\u30c8\u3092\u30d0\u30a4\u30ca\u30ea\u306b\u5909\u63db\u3059\u308b\u65b9\u6cd5\u3092\u8a18\u8ff0\u3057\u307e\u3059\u3002
  • Go\u8a00\u8a9e\u3067\u306f\u3001\u6587\u5b57\u5217\u306f\u4efb\u610f\u306e\u30d0\u30a4\u30c8\u306e\u4e0d\u5909\u30b9\u30e9\u30a4\u30b9\u3092\u53c2\u7167\u3057\u307e\u3059\u3002
  • Go\u8a00\u8a9e\u306e\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9\u306f UTF-8 \u3067\u30a8\u30f3\u30b3\u30fc\u30c9\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u3059\u3079\u306e\u6587\u5b57\u5217\u30ea\u30c6\u30e9\u30eb\u306f UTF-8 \u6587\u5b57\u5217\u3067\u3059\u3002\u305f\u3060\u3057\u3001\u6587\u5b57\u5217\u306b\u306f\u4efb\u610f\u306e\u30d0\u30a4\u30c8\u304c\u542b\u307e\u308c\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u305f\u3081\u3001\u6587\u5b57\u5217\u304c\uff08\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9\u3067\u306f\u306a\u3044\uff09\u4ed6\u306e\u5834\u6240\u304b\u3089\u53d6\u5f97\u3055\u308c\u305f\u5834\u5408\u3001\u305d\u306e\u6587\u5b57\u5217\u304c UTF-8 \u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u306b\u57fa\u3065\u3044\u3066\u3044\u308b\u4fdd\u8a3c\u306f\u3042\u308a\u307e\u305b\u3093\u3002
  • rune \u306f Unicode \u30b3\u30fc\u30c9\u30dd\u30a4\u30f3\u30c8\u306e\u6982\u5ff5\u306b\u5bfe\u5fdc\u3057\u3001\u5358\u4e00\u306e\u5024\u3067\u8868\u3055\u308c\u308b\u30a2\u30a4\u30c6\u30e0\u3092\u610f\u5473\u3057\u307e\u3059\u3002
  • UTF-8 \u3092\u4f7f\u7528\u3059\u308b\u3068\u3001Unicode \u30b3\u30fc\u30c9\u30dd\u30a4\u30f3\u30c8\u3092 1 \uff5e 4 \u30d0\u30a4\u30c8\u306b\u30a8\u30f3\u30b3\u30fc\u30c9\u3067\u304d\u307e\u3059\u3002
  • Go\u8a00\u8a9e\u3067\u6587\u5b57\u5217\u306b\u5bfe\u3057\u3066 len() \u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u30eb\u30fc\u30f3\u6570\u3067\u306f\u306a\u304f\u30d0\u30a4\u30c8\u6570\u304c\u8fd4\u3055\u308c\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#37","title":"\u6587\u5b57\u5217\u306b\u5bfe\u3059\u308b\u4e0d\u6b63\u306a\u53cd\u5fa9\u51e6\u7406 (#37)","text":"\u8981\u7d04

range \u6f14\u7b97\u5b50\u3092\u4f7f\u7528\u3057\u3066\u6587\u5b57\u5217\u3092\u53cd\u5fa9\u51e6\u7406\u3059\u308b\u3068\u3001\u30eb\u30fc\u30f3\u306e\u30d0\u30a4\u30c8\u30b7\u30fc\u30b1\u30f3\u30b9\u306e\u958b\u59cb\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306b\u5bfe\u5fdc\u3059\u308b\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u4f7f\u7528\u3057\u3066\u30eb\u30fc\u30f3\u304c\u53cd\u5fa9\u51e6\u7406\u3055\u308c\u307e\u3059\u3002\u7279\u5b9a\u306e\u30eb\u30fc\u30f3\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\uff08 3 \u756a\u76ee\u306e\u30eb\u30fc\u30f3\u306a\u3069\uff09\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u306b\u306f\u3001\u6587\u5b57\u5217\u3092 []rune \u306b\u5909\u63db\u3057\u307e\u3059\u3002

\u6587\u5b57\u5217\u306e\u53cd\u5fa9\u51e6\u7406\u306f\u3001\u958b\u767a\u8005\u306b\u3068\u3063\u3066\u4e00\u822c\u7684\u306a\u64cd\u4f5c\u3067\u3059\u3002\u304a\u305d\u3089\u304f\u3001\u6587\u5b57\u5217\u5185\u306e\u5404\u30eb\u30fc\u30f3\u306b\u5bfe\u3057\u3066\u64cd\u4f5c\u3092\u5b9f\u884c\u3059\u308b\u304b\u3001\u7279\u5b9a\u306e\u90e8\u5206\u6587\u5b57\u5217\u3092\u691c\u7d22\u3059\u308b\u72ec\u81ea\u306e\u95a2\u6570\u3092\u5b9f\u88c5\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3067\u3057\u3087\u3046\u3002\u3069\u3061\u3089\u306e\u5834\u5408\u3082\u3001\u6587\u5b57\u5217\u306e\u7570\u306a\u308b\u30eb\u30fc\u30f3\u3092\u53cd\u5fa9\u51e6\u7406\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3057\u304b\u3057\u3001\u53cd\u5fa9\u51e6\u7406\u304c\u3069\u306e\u3088\u3046\u306b\u6a5f\u80fd\u3059\u308b\u304b\u306b\u3064\u3044\u3066\u306f\u56f0\u60d1\u3057\u3084\u3059\u3044\u3067\u3059\u3002

\u6b21\u306e\u4f8b\u3092\u8003\u3048\u3066\u307f\u307e\u3057\u3087\u3046\u3002

s := \"h\u00eallo\"\nfor i := range s {\n    fmt.Printf(\"position %d: %c\\n\", i, s[i])\n}\nfmt.Printf(\"len=%d\\n\", len(s))\n
position 0: h\nposition 1: \u00c3\nposition 3: l\nposition 4: l\nposition 5: o\nlen=6\n

\u6df7\u4e71\u3092\u62db\u304f\u53ef\u80fd\u6027\u306e\u3042\u308b 3 \u70b9\u3092\u53d6\u308a\u4e0a\u3052\u307e\u3057\u3087\u3046\u3002

  • 2 \u756a\u76ee\u306e\u30eb\u30fc\u30f3\u306f\u3001\u51fa\u529b\u3067\u306f \u00ea \u3067\u306f\u306a\u304f \u00c3 \u306b\u306a\u308a\u307e\u3059\u3002
  • position 1 \u304b\u3089 position 3 \u306b\u30b8\u30e3\u30f3\u30d7\u3057\u307e\u3057\u305f\u3002 position 2 \u306b\u306f\u4f55\u304c\u3042\u308b\u306e\u3067\u3057\u3087\u3046\u304b\u3002
  • len \u306f 6 \u3092\u8fd4\u3057\u307e\u3059\u304c\u3001s \u306b\u306f 5 \u3064\u306e\u30eb\u30fc\u30f3\u3057\u304b\u542b\u307e\u308c\u3066\u3044\u307e\u305b\u3093\u3002

\u7d50\u679c\u306e\u6700\u5f8c\u304b\u3089\u898b\u3066\u3044\u304d\u307e\u3057\u3087\u3046\u3002len \u306f\u30eb\u30fc\u30f3\u6570\u3067\u306f\u306a\u304f\u3001\u6587\u5b57\u5217\u5185\u306e\u30d0\u30a4\u30c8\u6570\u3092\u8fd4\u3059\u3053\u3068\u306f\u3059\u3067\u306b\u8ff0\u3079\u307e\u3057\u305f\u3002\u6587\u5b57\u5217\u30ea\u30c6\u30e9\u30eb\u3092 s \u306b\u5272\u308a\u5f53\u3066\u3066\u3044\u308b\u305f\u3081\u3001s \u306f UTF-8 \u6587\u5b57\u5217\u3067\u3059\u3002\u4e00\u65b9\u3001\u7279\u6b8a\u6587\u5b57\u300c\u00ea\u300d\u306f 1 \u30d0\u30a4\u30c8\u3067\u30a8\u30f3\u30b3\u30fc\u30c9\u3055\u308c\u307e\u305b\u3093\u3002 2 \u30d0\u30a4\u30c8\u5fc5\u8981\u3067\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001len(s) \u3092\u547c\u3073\u51fa\u3059\u3068 6 \u304c\u8fd4\u3055\u308c\u307e\u3059\u3002

\u524d\u306e\u4f8b\u3067\u306f\u3001\u5404\u30eb\u30fc\u30f3\u3092\u53cd\u5fa9\u51e6\u7406\u3057\u3066\u3044\u306a\u3044\u3053\u3068\u3092\u7406\u89e3\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u4ee3\u308f\u308a\u306b\u3001\u30eb\u30fc\u30f3\u306e\u5404\u958b\u59cb\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u53cd\u5fa9\u51e6\u7406\u3057\u307e\u3059\u3002

s[i] \u3092\u51fa\u529b\u3057\u3066\u3082 i \u756a\u76ee\u306e\u30eb\u30fc\u30f3\u306f\u51fa\u529b\u3055\u308c\u307e\u305b\u3093\u3002\u30a4\u30f3\u30c7\u30c3\u30af\u30b9 i \u306e\u30d0\u30a4\u30c8\u306e UTF-8 \u8868\u73fe\u3092\u51fa\u529b\u3057\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001 h\u00eallo \u306e\u4ee3\u308f\u308a\u306b h\u00c3llo \u3092\u51fa\u529b\u304c\u3055\u308c\u307e\u3059\u3002

\u3055\u307e\u3056\u307e\u306a\u30eb\u30fc\u30f3\u6587\u5b57\u3092\u3059\u3079\u3066\u51fa\u529b\u3057\u305f\u3044\u5834\u5408\u306f\u3001 range \u6f14\u7b97\u5b50\u306e value \u8981\u7d20\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

s := \"h\u00eallo\"\nfor i, r := range s {\n    fmt.Printf(\"position %d: %c\\n\", i, r)\n}\n

\u307e\u305f\u306f\u3001\u6587\u5b57\u5217\u3092\u30eb\u30fc\u30f3\u306e\u30b9\u30e9\u30a4\u30b9\u306b\u5909\u63db\u3057\u3001\u305d\u308c\u3092\u53cd\u5fa9\u51e6\u7406\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002

s := \"h\u00eallo\"\nrunes := []rune(s)\nfor i, r := range runes {\n    fmt.Printf(\"position %d: %c\\n\", i, r)\n}\n

\u3053\u306e\u89e3\u6c7a\u7b56\u3067\u306f\u3001\u4ee5\u524d\u306e\u89e3\u6c7a\u7b56\u3068\u6bd4\u8f03\u3057\u3066\u5b9f\u884c\u6642\u306e\u30aa\u30fc\u30d0\u30fc\u30d8\u30c3\u30c9\u304c\u767a\u751f\u3059\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u5b9f\u969b\u3001\u6587\u5b57\u5217\u3092\u30eb\u30fc\u30f3\u306e\u30b9\u30e9\u30a4\u30b9\u306b\u5909\u63db\u3059\u308b\u306b\u306f\u3001\u8ffd\u52a0\u306e\u30b9\u30e9\u30a4\u30b9\u3092\u5272\u308a\u5f53\u3066\u3001\u30d0\u30a4\u30c8\u3092\u30eb\u30fc\u30f3\u306b\u5909\u63db\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u6587\u5b57\u5217\u306e\u30d0\u30a4\u30c8\u6570\u3092 n \u3068\u3059\u308b\u3068\u3001\u6642\u9593\u8a08\u7b97\u91cf\u306f O(n) \u306b\u306a\u308a\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u3059\u3079\u3066\u306e\u30eb\u30fc\u30f3\u3092\u53cd\u5fa9\u51e6\u7406\u3059\u308b\u5834\u5408\u306f\u3001\u6700\u521d\u306e\u89e3\u6c7a\u7b56\u3092\u4f7f\u7528\u3059\u308b\u3079\u304d\u3067\u3059\u3002

\u305f\u3060\u3057\u3001\u6700\u521d\u306e\u65b9\u6cd5\u3092\u4f7f\u7528\u3057\u3066\u6587\u5b57\u5217\u306e i \u756a\u76ee\u306e\u30eb\u30fc\u30f3\u306b\u30a2\u30af\u30bb\u30b9\u3057\u305f\u3044\u5834\u5408\u306f\u3001\u30eb\u30fc\u30f3\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u305b\u3093\u3002\u4ee3\u308f\u308a\u306b\u3001\u30d0\u30a4\u30c8\u30b7\u30fc\u30b1\u30f3\u30b9\u5185\u306e\u30eb\u30fc\u30f3\u306e\u958b\u59cb\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u304c\u308f\u304b\u308a\u307e\u3059\u3002

s := \"h\u00eallo\"\nr := []rune(s)[4]\nfmt.Printf(\"%c\\n\", r) // o\n

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#trim-38","title":"trim \u95a2\u6570\u306e\u8aa4\u7528 (#38)","text":"\u8981\u7d04

strings.TrimRight \u30fb strings.TrimLeft \u306f\u3001\u6307\u5b9a\u3055\u308c\u305f\u30bb\u30c3\u30c8\u306b\u542b\u307e\u308c\u308b\u3059\u3079\u3066\u306e\u672b\u5c3e\u30fb\u5148\u982d\u306e\u30eb\u30fc\u30f3\u3092\u524a\u9664\u3057\u307e\u3059\u304c\u3001 strings.TrimSuffix \u30fb strings.TrimPrefix \u306f\u3001\u6307\u5b9a\u3055\u308c\u305f\u63a5\u5c3e\u8f9e\u30fb\u63a5\u982d\u8f9e\u306e\u306a\u3044\u6587\u5b57\u5217\u3092\u8fd4\u3057\u307e\u3059\u3002

\u305f\u3068\u3048\u3070

fmt.Println(strings.TrimRight(\"123oxo\", \"xo\"))\n

\u306f 123 \u3092\u51fa\u529b\u3057\u307e\u3059

\u9006\u306b\u3001 strings.TrimLeft \u306f\u3001\u30bb\u30c3\u30c8\u306b\u542b\u307e\u308c\u308b\u5148\u982d\u306e\u30eb\u30fc\u30f3\u3092\u3059\u3079\u3066\u524a\u9664\u3057\u307e\u3059\u3002

\u4e00\u65b9\u3001strings.TrimSuffix \u30fb strings.TrimPrefix \u306f\u3001\u6307\u5b9a\u3055\u308c\u305f\u672b\u5c3e\u306e\u63a5\u5c3e\u8f9e\u30fb\u63a5\u982d\u8f9e\u3092\u9664\u3044\u305f\u6587\u5b57\u5217\u3092\u8fd4\u3057\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#39","title":"\u6700\u9069\u5316\u304c\u4e0d\u5341\u5206\u306a\u6587\u5b57\u5217\u306e\u9023\u7d50 (#39)","text":"\u8981\u7d04

\u6587\u5b57\u5217\u306e\u30ea\u30b9\u30c8\u306e\u9023\u7d50\u306f\u3001\u53cd\u5fa9\u3054\u3068\u306b\u65b0\u3057\u3044\u6587\u5b57\u5217\u304c\u5272\u308a\u5f53\u3066\u3089\u308c\u306a\u3044\u3088\u3046\u306b\u3001strings.Builder \u3092\u4f7f\u7528\u3057\u3066\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

+= \u6f14\u7b97\u5b50\u3092\u7528\u3044\u3066\u30b9\u30e9\u30a4\u30b9\u306e\u3059\u3079\u3066\u306e\u6587\u5b57\u5217\u8981\u7d20\u3092\u9023\u7d50\u3059\u308b concat \u95a2\u6570\u3092\u8003\u3048\u3066\u307f\u307e\u3057\u3087\u3046\u3002

func concat(values []string) string {\n    s := \"\"\n    for _, value := range values {\n        s += value\n    }\n    return s\n}\n

\u5404\u53cd\u5fa9\u4e2d\u306b\u3001 += \u6f14\u7b97\u5b50\u306f s \u3068 value \u6587\u5b57\u5217\u3092\u9023\u7d50\u3057\u307e\u3059\u3002\u4e00\u898b\u3059\u308b\u3068\u3001\u3053\u306e\u95a2\u6570\u306f\u9593\u9055\u3063\u3066\u3044\u306a\u3044\u3088\u3046\u306b\u898b\u3048\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002\u3057\u304b\u3057\u3001\u3053\u306e\u5b9f\u88c5\u306f\u3001\u6587\u5b57\u5217\u306e\u6838\u3068\u306a\u308b\u7279\u6027\u306e1\u3064\u3067\u3042\u308b\u4e0d\u5909\u6027\u3092\u5fd8\u308c\u3066\u3044\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u5404\u53cd\u5fa9\u3067\u306f s \u306f\u66f4\u65b0\u3055\u308c\u307e\u305b\u3093\u3002\u30e1\u30e2\u30ea\u5185\u306b\u65b0\u3057\u3044\u6587\u5b57\u5217\u3092\u518d\u5272\u308a\u5f53\u3066\u3059\u308b\u305f\u3081\u3001\u3053\u306e\u95a2\u6570\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306b\u5927\u304d\u306a\u5f71\u97ff\u3092\u4e0e\u3048\u307e\u3059\u3002

\u5e78\u3044\u306a\u3053\u3068\u306b\u3001 strings.Builder \u3092\u7528\u3044\u308b\u3053\u3068\u3067\u3001\u3053\u306e\u554f\u984c\u306b\u5bfe\u51e6\u3059\u308b\u89e3\u6c7a\u7b56\u304c\u3042\u308a\u307e\u3059\u3002

func concat(values []string) string {\n    sb := strings.Builder{}\n    for _, value := range values {\n        _, _ = sb.WriteString(value)\n    }\n    return sb.String()\n}\n

\u5404\u53cd\u5fa9\u4e2d\u306b\u3001value \u306e\u5185\u5bb9\u3092\u5185\u90e8\u30d0\u30c3\u30d5\u30a1\u306b\u8ffd\u52a0\u3059\u308b WriteString \u30e1\u30bd\u30c3\u30c9\u3092\u547c\u3073\u51fa\u3057\u3066\u7d50\u679c\u306e\u6587\u5b57\u5217\u3092\u69cb\u7bc9\u3057\u3001\u30e1\u30e2\u30ea\u306e\u30b3\u30d4\u30fc\u3092\u6700\u5c0f\u9650\u306b\u6291\u3048\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3057\u305f\u3002

\u88dc\u8db3

WriteString \u306f 2 \u756a\u76ee\u306e\u51fa\u529b\u3068\u3057\u3066\u30a8\u30e9\u30fc\u3092\u8fd4\u3057\u307e\u3059\u304c\u3001\u610f\u56f3\u7684\u306b\u7121\u8996\u3057\u307e\u3057\u3087\u3046\u3002\u5b9f\u969b\u3001\u3053\u306e\u30e1\u30bd\u30c3\u30c9\u306f nil \u30a8\u30e9\u30fc\u4ee5\u5916\u3092\u8fd4\u3059\u3053\u3068\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u3067\u306f\u3001\u3053\u306e\u30e1\u30bd\u30c3\u30c9\u304c\u30b7\u30b0\u30cd\u30c1\u30e3\u306e\u4e00\u90e8\u3068\u3057\u3066\u30a8\u30e9\u30fc\u3092\u8fd4\u3059\u76ee\u7684\u306f\u4f55\u3067\u3057\u3087\u3046\u304b\u3002strings.Builder \u306f io.StringWriter \u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u5b9f\u88c5\u3057\u3066\u304a\u308a\u3001\u3053\u308c\u306b\u306f WriteString(s string) (n int, err error) \u3068\u3044\u30461\u3064\u306e\u30e1\u30bd\u30c3\u30c9\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u3053\u306e\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u306b\u6e96\u62e0\u3059\u308b\u306b\u306f\u3001WriteString \u306f\u30a8\u30e9\u30fc\u3092\u8fd4\u3055\u306a\u3051\u308c\u3070\u306a\u3089\u306a\u3044\u306e\u3067\u3059\u3002

\u5185\u90e8\u7684\u306b\u306f\u3001strings.Builder \u306f\u30d0\u30a4\u30c8\u30b9\u30e9\u30a4\u30b9\u3092\u4fdd\u6301\u3057\u307e\u3059\u3002 WriteString \u3092\u547c\u3073\u51fa\u3059\u305f\u3073\u306b\u3001\u3053\u306e\u30b9\u30e9\u30a4\u30b9\u306b\u8ffd\u52a0\u3059\u308b\u547c\u3073\u51fa\u3057\u304c\u884c\u308f\u308c\u307e\u3059\u3002\u3053\u308c\u306b\u306f2\u3064\u306e\u5f71\u97ff\u304c\u3042\u308a\u307e\u3059\u3002\u307e\u305a\u3001 append \u306e\u547c\u3073\u51fa\u3057\u304c\u885d\u7a81\u72b6\u614b\u3092\u5f15\u304d\u8d77\u3053\u3059\u53ef\u80fd\u6027\u304c\u3042\u308b\u305f\u3081\u3001\u3053\u306e\u69cb\u9020\u4f53\u306f\u540c\u6642\u306b\u4f7f\u7528\u3055\u308c\u308b\u3079\u304d\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u30022\u756a\u76ee\u306e\u5f71\u97ff\u306f\u3001 \u975e\u52b9\u7387\u306a\u30b9\u30e9\u30a4\u30b9\u306e\u521d\u671f\u5316 (#21) \u3067\u898b\u305f\u3082\u306e\u3067\u3059\u3002\u30b9\u30e9\u30a4\u30b9\u306e\u5c06\u6765\u306e\u9577\u3055\u304c\u3059\u3067\u306b\u308f\u304b\u3063\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u305d\u308c\u3092\u4e8b\u524d\u306b\u5272\u308a\u5f53\u3066\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u306e\u305f\u3081\u306b\u3001strings.Builder \u306f\u5225\u306e n \u30d0\u30a4\u30c8\u306e\u305f\u3081\u306e\u30b9\u30da\u30fc\u30b9\u3092\u4fdd\u8a3c\u3059\u308b\u30e1\u30bd\u30c3\u30c9 Grow(n int) \u3092\u6301\u3063\u3066\u3044\u307e\u3059\u3002

func concat(values []string) string {\n    total := 0\n    for i := 0; i < len(values); i++ {\n        total += len(values[i])\n    }\n\n    sb := strings.Builder{}\n    sb.Grow(total) (2)\n    for _, value := range values {\n        _, _ = sb.WriteString(value)\n    }\n    return sb.String()\n}\n

\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u3092\u5b9f\u884c\u3057\u3066 3 \u3064\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\uff08 += \u3092\u4f7f\u7528\u3057\u305f V1 \u3001\u4e8b\u524d\u5272\u308a\u5f53\u3066\u306a\u3057\u3067 strings.Builder{} \u3092\u4f7f\u7528\u3057\u305f V2 \u3001\u4e8b\u524d\u5272\u308a\u5f53\u3066\u3042\u308a\u306e strings.Builder{} \u3092\u4f7f\u7528\u3057\u305f V3 \uff09\u3092\u6bd4\u8f03\u3057\u3066\u307f\u307e\u3057\u3087\u3046\u3002\u5165\u529b\u30b9\u30e9\u30a4\u30b9\u306b\u306f 1,000 \u500b\u306e\u6587\u5b57\u5217\u304c\u542b\u307e\u308c\u3066\u304a\u308a\u3001\u5404\u6587\u5b57\u5217\u306b\u306f 1,000 \u30d0\u30a4\u30c8\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059\u3002

BenchmarkConcatV1-4             16      72291485 ns/op\nBenchmarkConcatV2-4           1188        878962 ns/op\nBenchmarkConcatV3-4           5922        190340 ns/op\n

\u3054\u89a7\u306e\u3068\u304a\u308a\u3001\u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u6700\u3082\u52b9\u7387\u7684\u3067\u3001V1 \u3088\u308a 99% \u3001V2 \u3088\u308a 78% \u9ad8\u901f\u3067\u3059\u3002

strings.Builder \u306f\u3001\u6587\u5b57\u5217\u306e\u30ea\u30b9\u30c8\u3092\u9023\u7d50\u3059\u308b\u305f\u3081\u306e\u89e3\u6c7a\u7b56\u3068\u3057\u3066\u63a8\u5968\u3055\u308c\u307e\u3059\u3002\u901a\u5e38\u3001\u3053\u308c\u306f\u30eb\u30fc\u30d7\u5185\u3067\u4f7f\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3044\u304f\u3064\u304b\u306e\u6587\u5b57\u5217 \uff08\u540d\u524d\u3068\u59d3\u306a\u3069\uff09\u3092\u9023\u7d50\u3059\u308b\u3060\u3051\u306e\u5834\u5408\u3001 strings.Builder \u306e\u4f7f\u7528\u306f\u3001 += \u6f14\u7b97\u5b50\u3084 fmt.Sprintf \u3068\u6bd4\u3079\u3066\u53ef\u8aad\u6027\u304c\u4f4e\u304f\u306a\u308b\u304b\u3089\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#40","title":"\u7121\u99c4\u306a\u6587\u5b57\u5217\u5909\u63db (#40)","text":"\u8981\u7d04

bytes \u30d1\u30c3\u30b1\u30fc\u30b8\u306f strings \u30d1\u30c3\u30b1\u30fc\u30b8\u3068\u540c\u3058\u64cd\u4f5c\u3092\u63d0\u4f9b\u3057\u3066\u304f\u308c\u308b\u3053\u3068\u3092\u899a\u3048\u3066\u304a\u304f\u3068\u3001\u4f59\u5206\u306a\u30d0\u30a4\u30c8\u30fb\u6587\u5b57\u5217\u5909\u63db\u3092\u907f\u3051\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

\u6587\u5b57\u5217\u307e\u305f\u306f []byte \u3092\u6271\u3046\u3053\u3068\u3092\u9078\u629e\u3059\u308b\u5834\u5408\u3001\u307b\u3068\u3093\u3069\u306e\u30d7\u30ed\u30b0\u30e9\u30de\u30fc\u306f\u5229\u4fbf\u6027\u306e\u305f\u3081\u306b\u6587\u5b57\u5217\u3092\u597d\u3080\u50be\u5411\u304c\u3042\u308a\u307e\u3059\u3002\u3057\u304b\u3057\u3001\u307b\u3068\u3093\u3069\u306e I/O \u306f\u5b9f\u969b\u306b\u306f []byte \u3067\u884c\u308f\u308c\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001io.Reader\u3001io.Writer\u3001\u304a\u3088\u3073 io.ReadAll \u306f\u6587\u5b57\u5217\u3067\u306f\u306a\u304f []byte \u3092\u51e6\u7406\u3057\u307e\u3059\u3002

\u6587\u5b57\u5217\u3068 []byte \u306e\u3069\u3061\u3089\u3092\u6271\u3046\u3079\u304d\u304b\u8ff7\u3063\u305f\u3068\u304d\u3001[]byte \u3092\u6271\u3046\u65b9\u304c\u5fc5\u305a\u3057\u3082\u9762\u5012\u3060\u3068\u3044\u3046\u308f\u3051\u3067\u306f\u306a\u3044\u3053\u3068\u3092\u601d\u3044\u51fa\u3057\u3066\u304f\u3060\u3055\u3044\u3002strings \u30d1\u30c3\u30b1\u30fc\u30b8\u304b\u3089\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3055\u308c\u305f\u3059\u3079\u3066\u306e\u95a2\u6570\u306b\u306f\u3001bytes \u30d1\u30c3\u30b1\u30fc\u30b8\u306b\u4ee3\u66ff\u6a5f\u80fd\u304c\u3042\u308a\u307e\u3059\u3002 Split\u3001Count\u3001Contains\u3001Index \u306a\u3069\u3067\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001I/O \u3092\u5b9f\u884c\u3057\u3066\u3044\u308b\u304b\u3069\u3046\u304b\u306b\u95a2\u4fc2\u306a\u304f\u3001\u6587\u5b57\u5217\u306e\u4ee3\u308f\u308a\u306b\u30d0\u30a4\u30c8\u3092\u4f7f\u7528\u3057\u3066\u30ef\u30fc\u30af\u30d5\u30ed\u30fc\u5168\u4f53\u3092\u5b9f\u88c5\u3067\u304d\u3001\u8ffd\u52a0\u306e\u5909\u63db\u30b3\u30b9\u30c8\u3092\u56de\u907f\u3067\u304d\u308b\u304b\u3069\u3046\u304b\u3092\u6700\u521d\u306b\u78ba\u8a8d\u3057\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#41","title":"\u90e8\u5206\u6587\u5b57\u5217\u3068\u30e1\u30e2\u30ea\u30ea\u30fc\u30af (#41)","text":"\u8981\u7d04

\u90e8\u5206\u6587\u5b57\u5217\u306e\u4ee3\u308f\u308a\u306b\u30b3\u30d4\u30fc\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u90e8\u5206\u6587\u5b57\u5217\u64cd\u4f5c\u306b\u3088\u3063\u3066\u8fd4\u3055\u308c\u308b\u6587\u5b57\u5217\u304c\u540c\u3058\u30d0\u30a4\u30c8\u914d\u5217\u306b\u3088\u3063\u3066\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u308b\u305f\u3081\u3001\u30e1\u30e2\u30ea\u30ea\u30fc\u30af\u3092\u9632\u3050\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

\u30b9\u30e9\u30a4\u30b9\u3068\u30e1\u30e2\u30ea\u30ea\u30fc\u30af (#26) \u3067\u306f\u3001\u30b9\u30e9\u30a4\u30b9\u307e\u305f\u306f\u914d\u5217\u306e\u30b9\u30e9\u30a4\u30b9\u304c\u30e1\u30e2\u30ea\u30ea\u30fc\u30af\u306e\u72b6\u6cc1\u3092\u5f15\u304d\u8d77\u3053\u3059\u53ef\u80fd\u6027\u304c\u3042\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u307e\u3057\u305f\u3002\u3053\u306e\u539f\u5247\u306f\u3001\u6587\u5b57\u5217\u304a\u3088\u3073\u90e8\u5206\u6587\u5b57\u5217\u306e\u64cd\u4f5c\u306b\u3082\u5f53\u3066\u306f\u307e\u308a\u307e\u3059\u3002

Go\u8a00\u8a9e\u3067\u90e8\u5206\u6587\u5b57\u5217\u64cd\u4f5c\u3092\u4f7f\u7528\u3059\u308b\u3068\u304d\u306f\u30012 \u3064\u306e\u3053\u3068\u306b\u7559\u610f\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u307e\u305a\u3001\u63d0\u4f9b\u3055\u308c\u308b\u9593\u9694\u306f\u30eb\u30fc\u30f3\u6570\u3067\u306f\u306a\u304f\u3001\u30d0\u30a4\u30c8\u6570\u306b\u57fa\u3065\u3044\u3066\u3044\u307e\u3059\u3002\u6b21\u306b\u3001\u7d50\u679c\u306e\u90e8\u5206\u6587\u5b57\u5217\u304c\u6700\u521d\u306e\u6587\u5b57\u5217\u3068\u540c\u3058\u30d0\u30c3\u30ad\u30f3\u30b0\u914d\u5217\u3092\u5171\u6709\u3059\u308b\u305f\u3081\u3001\u90e8\u5206\u6587\u5b57\u5217\u64cd\u4f5c\u306b\u3088\u308a\u30e1\u30e2\u30ea\u30ea\u30fc\u30af\u304c\u767a\u751f\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u3053\u308c\u3092\u9632\u3050\u65b9\u6cd5\u306f\u3001\u6587\u5b57\u5217\u306e\u30b3\u30d4\u30fc\u3092\u624b\u52d5\u3067\u5b9f\u884c\u3059\u308b\u304b\u3001Go 1.18 \u304b\u3089\u5b9f\u88c5\u3055\u308c\u3066\u3044\u308b strings.Clone \u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#_7","title":"\u95a2\u6570\u3068\u30e1\u30bd\u30c3\u30c9","text":""},{"location":"ja/#42","title":"\u3069\u306e\u578b\u306e\u30ec\u30b7\u30fc\u30d0\u30fc\u3092\u4f7f\u7528\u3059\u308c\u3070\u3088\u3044\u304b\u308f\u304b\u3063\u3066\u3044\u306a\u3044 (#42)","text":"\u8981\u7d04

\u5024\u30ec\u30b7\u30fc\u30d0\u30fc\u3068\u30dd\u30a4\u30f3\u30bf\u30ec\u30b7\u30fc\u30d0\u30fc\u306e\u3069\u3061\u3089\u3092\u4f7f\u7528\u3059\u308b\u304b\u306f\u3001\u3069\u306e\u578b\u306a\u306e\u304b\u3001\u5909\u5316\u3055\u305b\u308b\u5fc5\u8981\u304c\u3042\u308b\u304b\u3069\u3046\u304b\u3001\u30b3\u30d4\u30fc\u3067\u304d\u306a\u3044\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u542b\u307e\u308c\u3066\u3044\u308b\u304b\u3069\u3046\u304b\u3001\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306f\u3069\u308c\u304f\u3089\u3044\u5927\u304d\u3044\u306e\u304b\u3001\u306a\u3069\u306e\u8981\u7d20\u306b\u57fa\u3065\u3044\u3066\u6c7a\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u5206\u304b\u3089\u306a\u3044\u5834\u5408\u306f\u3001\u30dd\u30a4\u30f3\u30bf\u30ec\u30b7\u30fc\u30d0\u3092\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u5024\u30ec\u30b7\u30fc\u30d0\u30fc\u3068\u30dd\u30a4\u30f3\u30bf\u30ec\u30b7\u30fc\u30d0\u30fc\u306e\u3069\u3061\u3089\u3092\u9078\u629e\u3059\u308b\u304b\u306f\u3001\u5fc5\u305a\u3057\u3082\u7c21\u5358\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u9078\u629e\u306b\u5f79\u7acb\u3064\u3044\u304f\u3064\u304b\u306e\u6761\u4ef6\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u307e\u3057\u3087\u3046\u3002

\u30dd\u30a4\u30f3\u30bf\u30ec\u30b7\u30fc\u30d0\u30fc\u3067\u306a\u3051\u308c\u3070\u306a\u3089\u306a\u3044 \u3068\u304d

  • \u30e1\u30bd\u30c3\u30c9\u304c\u30ec\u30b7\u30fc\u30d0\u30fc\u3092\u5909\u5316\u3055\u305b\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u3002\u3053\u306e\u30eb\u30fc\u30eb\u306f\u3001\u53d7\u4fe1\u5074\u304c\u30b9\u30e9\u30a4\u30b9\u3067\u3042\u308a\u3001\u30e1\u30bd\u30c3\u30c9\u304c\u8981\u7d20\u3092\u8ffd\u52a0\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306b\u3082\u6709\u52b9\u3067\u3059\u3002
type slice []int\n\nfunc (s *slice) add(element int) {\n    *s = append(*s, element)\n}\n
  • \u30e1\u30bd\u30c3\u30c9\u30ec\u30b7\u30fc\u30d0\u30fc\u306b\u30b3\u30d4\u30fc\u3067\u304d\u306a\u3044\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u542b\u307e\u308c\u3066\u3044\u308b\u5834\u5408\u3002sync \u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u578b\u90e8\u5206\u306f\u305d\u306e\u4e00\u4f8b\u306b\u306a\u308a\u307e\u3059\uff08 sync \u578b\u306e\u30b3\u30d4\u30fc (#74) \u3092\u53c2\u7167\uff09\u3002

\u30dd\u30a4\u30f3\u30bf\u30ec\u30b7\u30fc\u30d0\u30fc\u3067\u3042\u308b\u3079\u304d \u3068\u304d

  • \u30ec\u30b7\u30fc\u30d0\u30fc\u304c\u5927\u304d\u306a\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u5834\u5408\u3002\u30dd\u30a4\u30f3\u30bf\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u5927\u898f\u6a21\u306a\u30b3\u30d4\u30fc\u306e\u4f5c\u6210\u304c\u9632\u6b62\u3055\u308c\u308b\u305f\u3081\u3001\u547c\u3073\u51fa\u3057\u304c\u3088\u308a\u52b9\u7387\u7684\u306b\u306a\u308a\u307e\u3059\u3002\u3069\u308c\u304f\u3089\u3044\u306e\u5927\u304d\u3055\u306a\u306e\u304b\u78ba\u8a3c\u304c\u306a\u3044\u5834\u5408\u306f\u3001\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u304c\u89e3\u6c7a\u7b56\u306b\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u591a\u304f\u306e\u8981\u56e0\u306b\u4f9d\u5b58\u3059\u308b\u305f\u3081\u3001\u7279\u5b9a\u306e\u30b5\u30a4\u30ba\u3092\u6307\u5b9a\u3059\u308b\u3053\u3068\u306f\u307b\u3068\u3093\u3069\u4e0d\u53ef\u80fd\u3067\u3059\u3002

\u5024\u30ec\u30b7\u30fc\u30d0\u30fc\u3067\u306a\u3051\u308c\u3070\u306a\u3089\u306a\u3044 \u3068\u304d

  • \u30ec\u30b7\u30fc\u30d0\u30fc\u306e\u4e0d\u5909\u6027\u3092\u5f37\u5236\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u3002
  • \u30ec\u30b7\u30fc\u30d0\u30fc\u304c\u30de\u30c3\u30d7\u3001\u95a2\u6570\u3001\u30c1\u30e3\u30cd\u30eb\u306e\u5834\u5408\u3002\u305d\u308c\u4ee5\u5916\u306e\u5834\u5408\u306f\u30b3\u30f3\u30d1\u30a4\u30eb\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3059\u3002

\u5024\u30ec\u30b7\u30fc\u30d0\u30fc\u3067\u3042\u308b\u3079\u304d \u3068\u304d

  • \u30ec\u30b7\u30fc\u30d0\u30fc\u304c\u5909\u5316\u3055\u305b\u308b\u5fc5\u8981\u306e\u306a\u3044\u30b9\u30e9\u30a4\u30b9\u306e\u5834\u5408\u3002
  • \u30ec\u30b7\u30fc\u30d0\u30fc\u304c\u3001time.Time \u306a\u3069\u306e\u5c0f\u3055\u306a\u914d\u5217\u307e\u305f\u306f\u69cb\u9020\u4f53\u3067\u3001\u53ef\u5909\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u6301\u305f\u306a\u3044\u5024\u578b\u3067\u3042\u308b\u5834\u5408\u3002
  • \u30ec\u30b7\u30fc\u30d0\u30fc\u304c int\u3001float64\u3001\u307e\u305f\u306f string \u306a\u3069\u306e\u57fa\u672c\u578b\u306e\u5834\u5408\u3002

\u3082\u3061\u308d\u3093\u3001\u7279\u6b8a\u306a\u30b1\u30fc\u30b9\u306f\u5e38\u306b\u5b58\u5728\u3059\u308b\u305f\u3081\u3001\u3059\u3079\u3066\u3092\u7db2\u7f85\u3059\u308b\u3053\u3068\u306f\u4e0d\u53ef\u80fd\u3067\u3059\u304c\u3001\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u306e\u76ee\u6a19\u306f\u3001\u307b\u3068\u3093\u3069\u306e\u30b1\u30fc\u30b9\u3092\u30ab\u30d0\u30fc\u3059\u308b\u305f\u3081\u306e\u30ac\u30a4\u30c0\u30f3\u30b9\u3092\u63d0\u4f9b\u3059\u308b\u3053\u3068\u3067\u3059\u3002\u901a\u5e38\u306f\u3001\u305d\u3046\u3057\u306a\u3044\u6b63\u5f53\u306a\u7406\u7531\u304c\u306a\u3044\u9650\u308a\u3001\u5024\u30ec\u30b7\u30fc\u30d0\u30fc\u3092\u4f7f\u7528\u3057\u3066\u9593\u9055\u3044\u3042\u308a\u307e\u305b\u3093\u3002\u5206\u304b\u3089\u306a\u3044\u5834\u5408\u306f\u3001\u30dd\u30a4\u30f3\u30bf\u30ec\u30b7\u30fc\u30d0\u3092\u4f7f\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#43","title":"\u540d\u524d\u4ed8\u304d\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u3092\u307e\u3063\u305f\u304f\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044 (#43)","text":"\u8981\u7d04

\u540d\u524d\u4ed8\u304d\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u306e\u4f7f\u7528\u306f\u3001\u7279\u306b\u8907\u6570\u306e\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u304c\u540c\u3058\u578b\u3092\u6301\u3064\u5834\u5408\u3001\u95a2\u6570\u30fb\u30e1\u30bd\u30c3\u30c9\u306e\u8aad\u307f\u3084\u3059\u3055\u3092\u5411\u4e0a\u3055\u305b\u308b\u52b9\u7387\u7684\u306a\u65b9\u6cd5\u3067\u3059\u3002\u5834\u5408\u306b\u3088\u3063\u3066\u306f\u3001\u540d\u524d\u4ed8\u304d\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u306f\u30bc\u30ed\u5024\u306b\u521d\u671f\u5316\u3055\u308c\u308b\u305f\u3081\u3001\u3053\u306e\u65b9\u6cd5\u304c\u4fbf\u5229\u3067\u3059\u3089\u3042\u308b\u3053\u3068\u3082\u3042\u308a\u307e\u3059\u3002\u305f\u3060\u3057\u6f5c\u5728\u7684\u306a\u526f\u4f5c\u7528\u306b\u306f\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u95a2\u6570\u307e\u305f\u306f\u30e1\u30bd\u30c3\u30c9\u3067\u30d1\u30e9\u30e1\u30fc\u30bf\u3092\u8fd4\u3059\u3068\u304d\u3001\u3053\u308c\u3089\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u306b\u540d\u524d\u3092\u4ed8\u3051\u3066\u3001\u901a\u5e38\u306e\u5909\u6570\u3068\u3057\u3066\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u306b\u540d\u524d\u3092\u4ed8\u3051\u308b\u3068\u3001\u95a2\u6570\u30fb\u30e1\u30bd\u30c3\u30c9\u306e\u958b\u59cb\u6642\u306b\u305d\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u306f\u30bc\u30ed\u5024\u306b\u521d\u671f\u5316\u3055\u308c\u307e\u3059\u3002\u540d\u524d\u4ed8\u304d\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001 \u3080\u304d\u51fa\u3057\u306e return \u6587\uff08\u5f15\u6570\u306a\u3057\uff09 \u3092\u547c\u3073\u51fa\u3059\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002\u305d\u306e\u5834\u5408\u3001\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u306e\u73fe\u5728\u306e\u5024\u304c\u623b\u308a\u5024\u3068\u3057\u3066\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002

\u4ee5\u4e0b\u306f\u3001\u540d\u524d\u4ed8\u304d\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf b \u3092\u7528\u3044\u305f\u4f8b\u3067\u3059\u3002

func f(a int) (b int) {\n    b = a\n    return\n}\n

\u3053\u306e\u4f8b\u3067\u306f\u3001\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u306b\u540d\u524d b \u3092\u4ed8\u3051\u3066\u3044\u307e\u3059\u3002\u5f15\u6570\u306a\u3057\u3067 return \u3092\u547c\u3073\u51fa\u3059\u3068\u3001b \u306e\u73fe\u5728\u306e\u5024\u304c\u8fd4\u3055\u308c\u307e\u3059\u3002

\u5834\u5408\u306b\u3088\u3063\u3066\u306f\u3001\u540d\u524d\u4ed8\u304d\u306e\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u306b\u3088\u3063\u3066\u53ef\u8aad\u6027\u304c\u5411\u4e0a\u3059\u308b\u3053\u3068\u3082\u3042\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u30012 \u3064\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u304c\u540c\u3058\u578b\u3067\u3042\u308b\u5834\u5408\u306a\u3069\u3067\u3059\u3002\u305d\u306e\u4ed6\u306b\u3082\u3001\u5229\u4fbf\u6027\u306e\u305f\u3081\u306b\u7528\u3044\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u3086\u3048\u306b\u3001\u660e\u78ba\u306a\u5229\u70b9\u304c\u3042\u308b\u5834\u5408\u306f\u3001\u614e\u91cd\u306b\u306a\u308a\u306a\u304c\u3089\u3082\u540d\u524d\u4ed8\u304d\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u3092\u4f7f\u7528\u3059\u308b\u3079\u304d\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#44","title":"\u540d\u524d\u4ed8\u304d\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u306b\u3088\u308b\u4e88\u60f3\u5916\u306e\u526f\u4f5c\u7528 (#44)","text":"\u8981\u7d04

#43 \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u540d\u524d\u4ed8\u304d\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u304c\u72b6\u6cc1\u306b\u3088\u3063\u3066\u306f\u5f79\u7acb\u3064\u7406\u7531\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u307e\u3057\u305f\u3002 \u305f\u3060\u3057\u3001\u3053\u308c\u3089\u306f\u30bc\u30ed\u5024\u306b\u521d\u671f\u5316\u3055\u308c\u308b\u305f\u3081\u3001\u5341\u5206\u306b\u6ce8\u610f\u3057\u306a\u3044\u3068\u3001\u8efd\u5fae\u306a\u30d0\u30b0\u304c\u767a\u751f\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u3053\u306e\u30b3\u30fc\u30c9\u306f\u3069\u3053\u304c\u9593\u9055\u3063\u3066\u3044\u308b\u3067\u3057\u3087\u3046\u304b\u3002

func (l loc) getCoordinates(ctx context.Context, address string) (\n    lat, lng float32, err error) {\n    isValid := l.validateAddress(address) (1)\n    if !isValid {\n        return 0, 0, errors.New(\"invalid address\")\n    }\n\n    if ctx.Err() != nil { (2)\n        return 0, 0, err\n    }\n\n    // \u5ea7\u6a19\u3092\u53d6\u5f97\u3057\u3066\u8fd4\u3059\n}\n

\u4e00\u77a5\u3057\u305f\u3060\u3051\u3067\u306f\u30a8\u30e9\u30fc\u306f\u660e\u3089\u304b\u3067\u306f\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002if ctx.Err() != nil \u30b9\u30b3\u30fc\u30d7\u3067\u8fd4\u3055\u308c\u308b\u30a8\u30e9\u30fc\u306f err \u3067\u3059\u3002\u3057\u304b\u3057\u3001err \u5909\u6570\u306b\u306f\u5024\u3092\u5272\u308a\u5f53\u3066\u3066\u3044\u307e\u305b\u3093\u3002error \u578b\u306e\u30bc\u30ed\u5024\u3001 nil \u306b\u5272\u308a\u5f53\u3066\u3089\u308c\u305f\u307e\u307e\u3067\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u3053\u306e\u30b3\u30fc\u30c9\u306f\u5e38\u306b nil \u30a8\u30e9\u30fc\u3092\u8fd4\u3057\u307e\u3059\u3002

\u540d\u524d\u4ed8\u304d\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u3001\u5404\u30d1\u30e9\u30e1\u30fc\u30bf\u306f\u30bc\u30ed\u5024\u306b\u521d\u671f\u5316\u3055\u308c\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u8aac\u660e\u3057\u305f\u3088\u3046\u306b\u3001\u3053\u308c\u306b\u3088\u308a\u3001\u898b\u3064\u3051\u308b\u306e\u304c\u5fc5\u305a\u3057\u3082\u7c21\u5358\u3067\u306f\u306a\u3044\u8efd\u5fae\u306a\u30d0\u30b0\u304c\u767a\u751f\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u3086\u3048\u306b\u3001\u6f5c\u5728\u7684\u306a\u526f\u4f5c\u7528\u3092\u907f\u3051\u308b\u305f\u3081\u306b\u3001\u540d\u524d\u4ed8\u304d\u7d50\u679c\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u3092\u4f7f\u7528\u3059\u308b\u3068\u304d\u306f\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#nil-45","title":"nil \u30ec\u30b7\u30fc\u30d0\u30fc\u3092\u8fd4\u3059 (#45)","text":"\u8981\u7d04

\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u8fd4\u3059\u3068\u304d\u306f\u3001nil \u30dd\u30a4\u30f3\u30bf\u3092\u8fd4\u3059\u306e\u3067\u306f\u306a\u304f\u3001\u660e\u793a\u7684\u306a nil \u5024\u3092\u8fd4\u3059\u3088\u3046\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u305d\u3046\u3057\u306a\u3051\u308c\u3070\u3001\u610f\u56f3\u3057\u306a\u3044\u7d50\u679c\u304c\u767a\u751f\u3057\u3001\u547c\u3073\u51fa\u3057\u5143\u304c nil \u3067\u306f\u306a\u3044\u5024\u3092\u53d7\u3051\u53d6\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#46","title":"\u95a2\u6570\u5165\u529b\u306b\u30d5\u30a1\u30a4\u30eb\u540d\u3092\u4f7f\u7528\u3057\u3066\u3044\u308b (#46)","text":"\u8981\u7d04

\u30d5\u30a1\u30a4\u30eb\u540d\u306e\u4ee3\u308f\u308a\u306b io.Reader \u578b\u3092\u53d7\u3051\u53d6\u308b\u3088\u3046\u306b\u95a2\u6570\u3092\u8a2d\u8a08\u3059\u308b\u3068\u3001\u95a2\u6570\u306e\u518d\u5229\u7528\u6027\u304c\u5411\u4e0a\u3057\u3001\u30c6\u30b9\u30c8\u304c\u5bb9\u6613\u306b\u306a\u308a\u307e\u3059\u3002

\u30d5\u30a1\u30a4\u30eb\u540d\u3092\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u8aad\u307f\u53d6\u308b\u305f\u3081\u306e\u95a2\u6570\u5165\u529b\u3068\u3057\u3066\u53d7\u3051\u5165\u308c\u308b\u3053\u3068\u306f\u3001\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u3001\u300c\u30b3\u30fc\u30c9\u306e\u81ed\u3044\u300d\u3068\u307f\u306a\u3055\u308c\u308b\u3079\u304d\u3067\u3059\uff08 os.Open \u306a\u3069\u306e\u7279\u5b9a\u306e\u95a2\u6570\u3092\u9664\u304f\uff09\u3002\u8907\u6570\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u4f5c\u6210\u3059\u308b\u3053\u3068\u306b\u306b\u306a\u308b\u304b\u3082\u3057\u308c\u305a\u3001\u5358\u4f53\u30c6\u30b9\u30c8\u304c\u3088\u308a\u8907\u96d1\u306b\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u304b\u3089\u3067\u3059\u3002\u307e\u305f\u3001\u95a2\u6570\u306e\u518d\u5229\u7528\u6027\u3082\u4f4e\u4e0b\u3057\u307e\u3059 \uff08\u305f\u3060\u3057\u3001\u3059\u3079\u3066\u306e\u95a2\u6570\u304c\u518d\u5229\u7528\u3055\u308c\u308b\u308f\u3051\u3067\u306f\u3042\u308a\u307e\u305b\u3093\uff09\u3002 io.Reader \u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u62bd\u8c61\u5316\u3055\u308c\u307e\u3059\u3002\u5165\u529b\u304c\u30d5\u30a1\u30a4\u30eb\u3001\u6587\u5b57\u5217\u3001HTTP \u30ea\u30af\u30a8\u30b9\u30c8\u3001gRPC \u30ea\u30af\u30a8\u30b9\u30c8\u306e\u3044\u305a\u308c\u3067\u3042\u308b\u304b\u306b\u95a2\u4fc2\u306a\u304f\u3001\u5b9f\u88c5\u306f\u518d\u5229\u7528\u3067\u304d\u3001\u7c21\u5358\u306b\u30c6\u30b9\u30c8\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#defer-47","title":"defer \u5f15\u6570\u3068\u30ec\u30b7\u30fc\u30d0\u30fc\u304c\u3069\u306e\u3088\u3046\u306b\u8a55\u4fa1\u3055\u308c\u308b\u304b\u3092\u77e5\u3089\u306a\u3044\uff08\u5f15\u6570\u306e\u8a55\u4fa1\u3001\u30dd\u30a4\u30f3\u30bf\u30fc\u3001\u304a\u3088\u3073\u5024\u30ec\u30b7\u30fc\u30d0\u30fc\uff09 (#47)","text":"\u8981\u7d04

\u30dd\u30a4\u30f3\u30bf\u3092 defer \u95a2\u6570\u306b\u6e21\u3059\u3053\u3068\u3068\u3001\u547c\u3073\u51fa\u3057\u3092\u30af\u30ed\u30fc\u30b8\u30e3\u5185\u306b\u30e9\u30c3\u30d7\u3059\u308b\u3053\u3068\u304c\u3001\u5f15\u6570\u3068\u30ec\u30b7\u30fc\u30d0\u30fc\u306e\u5373\u6642\u8a55\u4fa1\u3092\u514b\u670d\u3059\u308b\u305f\u3081\u306b\u5b9f\u73fe\u53ef\u80fd\u306a\u89e3\u6c7a\u7b56\u3067\u3059\u3002

defer \u95a2\u6570\u3067\u306f\u3001\u5f15\u6570\u306f\u3001\u4e0a\u4f4d\u30d6\u30ed\u30c3\u30af\u306e\u95a2\u6570\u304c\u623b\u3063\u3066\u304b\u3089\u3067\u306f\u306a\u304f\u3001\u3059\u3050\u306b\u8a55\u4fa1\u3055\u308c\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u3053\u306e\u30b3\u30fc\u30c9\u3067\u306f\u3001\u5e38\u306b\u540c\u3058\u30b9\u30c6\u30fc\u30bf\u30b9\u2015\u2015\u7a7a\u306e\u6587\u5b57\u5217\u2015\u2015\u3067 notify \u3068 incrementCounter \u3092\u547c\u3073\u51fa\u3057\u307e\u3059\u3002

const (\n    StatusSuccess  = \"success\"\n    StatusErrorFoo = \"error_foo\"\n    StatusErrorBar = \"error_bar\"\n)\n\nfunc f() error {\n    var status string\n    defer notify(status)\n    defer incrementCounter(status)\n\n    if err := foo(); err != nil {\n        status = StatusErrorFoo\n        return err\n    }\n\n    if err := bar(); err != nil {\n        status = StatusErrorBar\n        return err\n    }\n\n    status = StatusSuccess\n    return nil\n}\n

\u305f\u3057\u304b\u306b\u3001notify(status) \u3068 incrementCounter(status) \u3092 defer \u95a2\u6570\u3068\u3057\u3066\u547c\u3073\u51fa\u3057\u3066\u3044\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001Go\u8a00\u8a9e\u306f\u3001defer \u3092\u4f7f\u7528\u3057\u305f\u6bb5\u968e\u3067 f \u304c\u30b9\u30c6\u30fc\u30bf\u30b9\u306e\u73fe\u5728\u306e\u5024\u3092\u8fd4\u3059\u3068\u3001\u3053\u308c\u3089\u306e\u547c\u3073\u51fa\u3057\u306e\u5b9f\u884c\u3092\u9045\u3089\u305b\u3001\u7a7a\u306e\u6587\u5b57\u5217\u3092\u6e21\u3057\u307e\u3059\u3002

defer \u3092\u4f7f\u3044\u7d9a\u3051\u305f\u3044\u5834\u5408\u306e\u4e3b\u306a\u65b9\u6cd5\u306f 2 \u3064\u3042\u308a\u307e\u3059\u3002

\u6700\u521d\u306e\u89e3\u6c7a\u7b56\u306f\u6587\u5b57\u5217\u30dd\u30a4\u30f3\u30bf\u3092\u6e21\u3059\u3053\u3068\u3067\u3059\u3002

func f() error {\n    var status string\n    defer notify(&status) \n    defer incrementCounter(&status)\n\n    // \u95a2\u6570\u306e\u305d\u308c\u4ee5\u5916\u306e\u90e8\u5206\u306f\u5909\u66f4\u306a\u3057\n}\n

defer \u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u5f15\u6570\uff08\u3053\u3053\u3067\u306f\u30b9\u30c6\u30fc\u30bf\u30b9\u306e\u30a2\u30c9\u30ec\u30b9\uff09\u304c\u3059\u3050\u306b\u8a55\u4fa1\u3055\u308c\u307e\u3059\u3002\u30b9\u30c6\u30fc\u30bf\u30b9\u81ea\u4f53\u306f\u95a2\u6570\u5168\u4f53\u3067\u5909\u66f4\u3055\u308c\u307e\u3059\u304c\u3001\u305d\u306e\u30a2\u30c9\u30ec\u30b9\u306f\u5272\u308a\u5f53\u3066\u306b\u95a2\u4fc2\u306a\u304f\u4e00\u5b9a\u306e\u307e\u307e\u3067\u3059\u3002\u3088\u3063\u3066\u3001notify \u307e\u305f\u306f incrementCounter \u304c\u6587\u5b57\u5217\u30dd\u30a4\u30f3\u30bf\u306b\u3088\u3063\u3066\u53c2\u7167\u3055\u308c\u308b\u5024\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u3001\u671f\u5f85\u3069\u304a\u308a\u306b\u52d5\u4f5c\u3057\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u3053\u306e\u89e3\u6c7a\u7b56\u3067\u306f 2 \u3064\u306e\u95a2\u6570\u306e\u30b7\u30b0\u30cd\u30c1\u30e3\u3092\u5909\u66f4\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u3001\u305d\u308c\u304c\u5e38\u306b\u53ef\u80fd\u3067\u3042\u308b\u3068\u306f\u9650\u308a\u307e\u305b\u3093\u3002

\u5225\u306e\u89e3\u6c7a\u7b56\u304c\u3042\u308a\u307e\u3059\u2015\u2015\u30af\u30ed\u30fc\u30b8\u30e3\uff08\u672c\u4f53\u306e\u5916\u90e8\u304b\u3089\u5909\u6570\u3092\u53c2\u7167\u3059\u308b\u533f\u540d\u95a2\u6570\u5024\uff09\u3092 defer \u6587\u3068\u3057\u3066\u547c\u3073\u51fa\u3059\u3053\u3068\u3067\u3059\u3002

func f() error {\n    var status string\n    defer func() {\n        notify(status)\n        incrementCounter(status)\n    }()\n\n    // \u95a2\u6570\u306e\u305d\u308c\u4ee5\u5916\u306e\u90e8\u5206\u306f\u5909\u66f4\u306a\u3057\n}\n

\u3053\u3053\u3067\u306f\u3001notify \u3068 incrementCounter \u306e\u4e21\u65b9\u306e\u547c\u3073\u51fa\u3057\u3092\u30af\u30ed\u30fc\u30b8\u30e3\u5185\u306b\u30e9\u30c3\u30d7\u3057\u307e\u3059\u3002\u3053\u306e\u30af\u30ed\u30fc\u30b8\u30e3\u306f\u3001\u672c\u4f53\u306e\u5916\u90e8\u304b\u3089\u30b9\u30c6\u30fc\u30bf\u30b9\u5909\u6570\u3092\u53c2\u7167\u3057\u307e\u3059\u3002\u3086\u3048\u306b\u3001status \u306f\u3001defer \u3092\u547c\u3073\u51fa\u3057\u305f\u3068\u304d\u3067\u306f\u306a\u304f\u3001\u30af\u30ed\u30fc\u30b8\u30e3\u304c\u5b9f\u884c\u3055\u308c\u305f\u3068\u304d\u306b\u8a55\u4fa1\u3055\u308c\u307e\u3059\u3002\u3053\u306e\u89e3\u6c7a\u7b56\u306f\u6b63\u3057\u304f\u6a5f\u80fd\u3059\u308b\u4e0a\u306b\u3001\u30b7\u30b0\u30cd\u30c1\u30e3\u3092\u5909\u66f4\u3059\u308b\u305f\u3081\u306b notify \u3084 incrementCounter \u3092\u5fc5\u8981\u3068\u3057\u307e\u305b\u3093\u3002

\u3053\u306e\u52d5\u4f5c\u306f\u30e1\u30bd\u30c3\u30c9\u30ec\u30b7\u30fc\u30d0\u30fc\u306b\u3082\u9069\u7528\u3055\u308c\u308b\u3053\u3068\u306b\u3082\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30ec\u30b7\u30fc\u30d0\u30fc\u306f\u3059\u3050\u306b\u8a55\u4fa1\u3055\u308c\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#_8","title":"\u30a8\u30e9\u30fc\u51e6\u7406","text":""},{"location":"ja/#48","title":"\u30d1\u30cb\u30c3\u30af (#48)","text":"\u8981\u7d04

panic \u306e\u4f7f\u7528\u306f\u3001Go\u8a00\u8a9e\u3067\u30a8\u30e9\u30fc\u306b\u5bfe\u51e6\u3059\u308b\u305f\u3081\u306e\u624b\u6bb5\u3067\u3059\u3002\u305f\u3060\u3057\u3001\u3053\u308c\u306f\u56de\u5fa9\u4e0d\u80fd\u306a\u72b6\u6cc1\u3067\u306e\u307f\u4f7f\u7528\u3059\u308b\u3088\u3046\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u305f\u3068\u3048\u3070\u3001\u30d2\u30e5\u30fc\u30de\u30f3\u30a8\u30e9\u30fc\u3092\u901a\u77e5\u3059\u308b\u5834\u5408\u3084\u3001\u5fc5\u9808\u306e\u4f9d\u5b58\u95a2\u4fc2\u306e\u8aad\u307f\u8fbc\u307f\u306b\u5931\u6557\u3057\u305f\u5834\u5408\u306a\u3069\u3067\u3059\u3002

Go\u8a00\u8a9e\u3067\u306f\u3001panic \u306f\u901a\u5e38\u306e\u6d41\u308c\u3092\u505c\u6b62\u3059\u308b\u7d44\u307f\u8fbc\u307f\u95a2\u6570\u3067\u3059\u3002

func main() {\n    fmt.Println(\"a\")\n    panic(\"foo\")\n    fmt.Println(\"b\")\n}\n

\u3053\u306e\u30b3\u30fc\u30c9\u306f a \u3092\u51fa\u529b\u3057\u3001b \u3092\u51fa\u529b\u3059\u308b\u524d\u306b\u505c\u6b62\u3057\u307e\u3059\u3002

a\npanic: foo\n\ngoroutine 1 [running]:\nmain.main()\n        main.go:7 +0xb3\n

panic \u306e\u4f7f\u7528\u306f\u614e\u91cd\u306b\u3059\u3079\u304d\u3067\u3059\u3002\u4ee3\u8868\u7684\u306a\u30b1\u30fc\u30b9\u304c 2 \u3064\u3042\u308a\u30011 \u3064\u306f\u30d2\u30e5\u30fc\u30de\u30f3\u30a8\u30e9\u30fc\u3092\u901a\u77e5\u3059\u308b\u5834\u5408\uff08\u4f8b: sql.Register\u30c9\u30e9\u30a4\u30d0\u30fc\u304c nil \u307e\u305f\u306f\u65e2\u306b\u767b\u9332\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u306b panic \u3092\u8d77\u3053\u3057\u307e\u3059\uff09\u3001\u3082\u3046 1 \u3064\u306f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u5fc5\u9808\u306e\u4f9d\u5b58\u95a2\u4fc2\u306e\u751f\u6210\u306b\u5931\u6557\u3057\u305f\u5834\u5408\u3067\u3059\u3002\u7d50\u679c\u3068\u3057\u3066\u3001\u4f8b\u5916\u7684\u306b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u505c\u6b62\u3057\u307e\u3059\u3002\u305d\u308c\u4ee5\u5916\u306e\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u306b\u304a\u3044\u3066\u306f\u3001\u30a8\u30e9\u30fc\u51e6\u7406\u306f\u3001\u6700\u5f8c\u306e\u623b\u308a\u5f15\u6570\u3068\u3057\u3066\u9069\u5207\u306a\u30a8\u30e9\u30fc\u578b\u3092\u8fd4\u3059\u95a2\u6570\u3092\u901a\u3058\u3066\u884c\u3046\u3079\u304d\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#49","title":"\u30a8\u30e9\u30fc\u3092\u30e9\u30c3\u30d7\u3059\u3079\u304d\u3068\u304d\u3092\u77e5\u3089\u306a\u3044 (#49)","text":"\u8981\u7d04

\u30a8\u30e9\u30fc\u3092\u30e9\u30c3\u30d7\u3059\u308b\u3068\u3001\u30a8\u30e9\u30fc\u3092\u30de\u30fc\u30af\u3057\u305f\u308a\u3001\u8ffd\u52a0\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3092\u63d0\u4f9b\u3057\u305f\u308a\u3067\u304d\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u30a8\u30e9\u30fc\u30e9\u30c3\u30d4\u30f3\u30b0\u306b\u3088\u308a\u3001\u547c\u3073\u51fa\u3057\u5143\u304c\u30bd\u30fc\u30b9\u30a8\u30e9\u30fc\u3092\u5229\u7528\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308b\u305f\u3081\u3001\u6f5c\u5728\u7684\u306a\u7d50\u5408\u304c\u767a\u751f\u3057\u307e\u3059\u3002\u305d\u308c\u3092\u907f\u3051\u305f\u3044\u5834\u5408\u306f\u3001\u30a8\u30e9\u30fc\u30e9\u30c3\u30d4\u30f3\u30b0\u3092\u4f7f\u7528\u3057\u306a\u3044\u3067\u304f\u3060\u3055\u3044\u3002

Go 1.13 \u4ee5\u964d\u3001%w \u30c7\u30a3\u30ec\u30af\u30c6\u30a3\u30d6\u3092\u4f7f\u7528\u3059\u308c\u3070\u7c21\u5358\u306b\u30a8\u30e9\u30fc\u3092\u30e9\u30c3\u30d7\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3057\u305f\u3002\u30a8\u30e9\u30fc\u30e9\u30c3\u30d4\u30f3\u30b0\u3068\u306f\u3001\u30bd\u30fc\u30b9\u30a8\u30e9\u30fc\u3082\u4f7f\u7528\u3067\u304d\u308b\u3088\u3046\u306b\u3059\u308b\u30e9\u30c3\u30d1\u30fc\u30b3\u30f3\u30c6\u30ca\u5185\u3067\u30a8\u30e9\u30fc\u3092\u30e9\u30c3\u30d7\u307e\u305f\u306f\u30d1\u30c3\u30af\u3059\u308b\u3053\u3068\u3067\u3059\u3002\u4e00\u822c\u306b\u3001\u30a8\u30e9\u30fc\u30e9\u30c3\u30d4\u30f3\u30b0\u306e\u4e3b\u306a\u4f7f\u7528\u4f8b\u306f\u6b21\u306e 2 \u3064\u3067\u3059\u3002

  • \u30a8\u30e9\u30fc\u306b\u3055\u3089\u306b\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3092\u52a0\u3048\u308b
  • \u30a8\u30e9\u30fc\u3092\u7279\u5b9a\u306e\u30a8\u30e9\u30fc\u3068\u3057\u3066\u30de\u30fc\u30af\u3059\u308b

\u30a8\u30e9\u30fc\u3092\u51e6\u7406\u3059\u308b\u3068\u304d\u3001\u30a8\u30e9\u30fc\u3092\u30e9\u30c3\u30d7\u3059\u308b\u304b\u3069\u3046\u304b\u3092\u6c7a\u5b9a\u3067\u304d\u307e\u3059\u3002\u30e9\u30c3\u30d4\u30f3\u30b0\u3068\u306f\u3001\u30a8\u30e9\u30fc\u306b\u3055\u3089\u306b\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3092\u8ffd\u52a0\u3057\u305f\u308a\u3001\u30a8\u30e9\u30fc\u3092\u7279\u5b9a\u306e\u30bf\u30a4\u30d7\u3068\u3057\u3066\u30de\u30fc\u30af\u3057\u305f\u308a\u3059\u308b\u3053\u3068\u3067\u3059\u3002\u30a8\u30e9\u30fc\u3092\u30de\u30fc\u30af\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306f\u3001\u72ec\u81ea\u306e\u30a8\u30e9\u30fc\u578b\u3092\u4f5c\u6210\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3067\u3059\u304c\u3001\u65b0\u305f\u306b\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3092\u52a0\u3048\u305f\u3044\u3060\u3051\u306e\u5834\u5408\u306f\u3001\u65b0\u3057\u3044\u30a8\u30e9\u30fc\u578b\u3092\u4f5c\u6210\u3059\u308b\u5fc5\u8981\u304c\u306a\u3044\u305f\u3081\u3001%w \u30c7\u30a3\u30ec\u30af\u30c6\u30a3\u30d6\u3092\u6307\u5b9a\u3057\u3066 fmt.Errorf \u3092\u4f7f\u7528\u3057\u307e\u3057\u3087\u3046\u3002\u305f\u3060\u3057\u3001\u30a8\u30e9\u30fc\u30e9\u30c3\u30d4\u30f3\u30b0\u306b\u3088\u308a\u3001\u547c\u3073\u51fa\u3057\u5143\u304c\u30bd\u30fc\u30b9\u30a8\u30e9\u30fc\u3092\u5229\u7528\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308b\u305f\u3081\u3001\u6f5c\u5728\u7684\u306a\u7d50\u5408\u304c\u751f\u3058\u307e\u3059\u3002\u305d\u308c\u3092\u907f\u3051\u305f\u3044\u5834\u5408\u306f\u3001\u30a8\u30e9\u30fc\u306e\u30e9\u30c3\u30d4\u30f3\u30b0\u3067\u306f\u306a\u304f\u3001\u30a8\u30e9\u30fc\u306e\u5909\u63db\u3092\u4f7f\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001%v \u30c7\u30a3\u30ec\u30af\u30c6\u30a3\u30d6\u3092\u6307\u5b9a\u3057\u305f fmt.Errorf \u3092\u4f7f\u7528\u3057\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#50","title":"\u30a8\u30e9\u30fc\u578b\u306e\u4e0d\u6b63\u306a\u6bd4\u8f03 (#50)","text":"\u8981\u7d04

Go 1.13 \u306e\u30a8\u30e9\u30fc\u30e9\u30c3\u30d4\u30f3\u30b0\u3092 %w \u30c7\u30a3\u30ec\u30af\u30c6\u30a3\u30d6\u3068 fmt.Errorf \u3067\u4f7f\u7528\u3059\u308b\u5834\u5408\u3001\u578b\u306b\u5bfe\u3059\u308b\u30a8\u30e9\u30fc\u306e\u6bd4\u8f03\u306f errors.As \u3092\u901a\u3058\u3066\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u3046\u3067\u306a\u3051\u308c\u3070\u3001\u8fd4\u3055\u308c\u305f\u30a8\u30e9\u30fc\u304c\u30e9\u30c3\u30d7\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u3001\u8a55\u4fa1\u306b\u5931\u6557\u3057\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#51","title":"\u30a8\u30e9\u30fc\u5024\u306e\u4e0d\u6b63\u306a\u6bd4\u8f03 (#51)","text":"\u8981\u7d04

Go 1.13 \u306e\u30a8\u30e9\u30fc\u30e9\u30c3\u30d4\u30f3\u30b0\u3092 %w \u30c7\u30a3\u30ec\u30af\u30c6\u30a3\u30d6\u3068 fmt.Errorf \u3067\u4f7f\u7528\u3059\u308b\u5834\u5408\u3001\u30a8\u30e9\u30fc\u3068\u5024\u306e\u6bd4\u8f03\u306f errors.As \u3092\u901a\u3058\u3066\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u3046\u3067\u306a\u3051\u308c\u3070\u3001\u8fd4\u3055\u308c\u305f\u30a8\u30e9\u30fc\u304c\u30e9\u30c3\u30d7\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u3001\u8a55\u4fa1\u306b\u5931\u6557\u3057\u307e\u3059\u3002

\u30bb\u30f3\u30c1\u30cd\u30eb\u30a8\u30e9\u30fc\u306f\u30b0\u30ed\u30fc\u30d0\u30eb\u5909\u6570\u3068\u3057\u3066\u5b9a\u7fa9\u3055\u308c\u305f\u30a8\u30e9\u30fc\u306e\u3053\u3068\u3067\u3059\u3002

import \"errors\"\n\nvar ErrFoo = errors.New(\"foo\")\n
\u4e00\u822c\u306b\u3001\u6163\u4f8b\u3068\u3057\u3066 Err \u3067\u59cb\u3081\u3001\u305d\u306e\u5f8c\u306b\u30a8\u30e9\u30fc\u578b\u3092\u7d9a\u3051\u307e\u3059\u3002\u3053\u3053\u3067\u306f ErrFoo \u3067\u3059\u3002\u30bb\u30f3\u30c1\u30cd\u30eb\u30a8\u30e9\u30fc\u306f\u3001\u4e88\u671f\u3055\u308c\u308b \u30a8\u30e9\u30fc\u3001\u3064\u307e\u308a\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u78ba\u8a8d\u3059\u308b\u3053\u3068\u3092\u671f\u5f85\u3059\u308b\u30a8\u30e9\u30fc\u3092\u4f1d\u3048\u307e\u3059\u3002\u4e00\u822c\u7684\u306a\u30ac\u30a4\u30c9\u30e9\u30a4\u30f3\u3068\u3057\u3066

  • \u4e88\u671f\u3055\u308c\u308b\u30a8\u30e9\u30fc\u306f\u30a8\u30e9\u30fc\u5024\uff08\u30bb\u30f3\u30c1\u30cd\u30eb\u30a8\u30e9\u30fc\uff09\u3068\u3057\u3066\u8a2d\u8a08\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\uff1a var ErrFoo =errors.New(\"foo\")\u3002
  • \u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc\u306f\u30a8\u30e9\u30fc\u578b\u3068\u3057\u3066\u8a2d\u8a08\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\uff1a BarError \u306f error \u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u5b9f\u88c5\u3057\u305f\u4e0a\u3067 type BarError struct { ... }\u3002

\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067 %w \u30c7\u30a3\u30ec\u30af\u30c6\u30a3\u30d6\u3068 fmt.Errorf \u3092\u4f7f\u7528\u3057\u3066\u30a8\u30e9\u30fc\u30e9\u30c3\u30d7\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u3001\u7279\u5b9a\u306e\u5024\u306b\u5bfe\u3059\u308b\u30a8\u30e9\u30fc\u306e\u30c1\u30a7\u30c3\u30af\u306f == \u306e\u4ee3\u308f\u308a\u306b errors.Is \u3092\u4f7f\u7528\u3057\u3066\u884c\u3044\u307e\u3057\u3087\u3046\u3002\u305d\u308c\u306b\u3088\u3063\u3066\u3001\u30bb\u30f3\u30c1\u30cd\u30eb\u30a8\u30e9\u30fc\u304c\u30e9\u30c3\u30d7\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u3067\u3082\u3001errors.Is \u306f\u305d\u308c\u3092\u518d\u5e30\u7684\u306b\u30a2\u30f3\u30e9\u30c3\u30d7\u3057\u3001\u30c1\u30a7\u30fc\u30f3\u5185\u306e\u5404\u30a8\u30e9\u30fc\u3092\u63d0\u4f9b\u3055\u308c\u305f\u5024\u3068\u6bd4\u8f03\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#2-52","title":"\u30a8\u30e9\u30fc\u306e 2 \u56de\u51e6\u7406 (#52)","text":"\u8981\u7d04

\u307b\u3068\u3093\u3069\u306e\u5834\u5408\u3001\u30a8\u30e9\u30fc\u306f 1 \u56de\u3067\u51e6\u7406\u3055\u308c\u308b\u3079\u304d\u3067\u3059\u3002\u30a8\u30e9\u30fc\u3092\u30ed\u30b0\u306b\u8a18\u9332\u3059\u308b\u3053\u3068\u306f\u3001\u30a8\u30e9\u30fc\u3092\u51e6\u7406\u3059\u308b\u3053\u3068\u3067\u3059\u3002\u3059\u306a\u308f\u3061\u3001\u30ed\u30b0\u306b\u8a18\u9332\u3059\u308b\u304b\u30a8\u30e9\u30fc\u3092\u8fd4\u3059\u304b\u3092\u9078\u629e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u591a\u304f\u306e\u5834\u5408\u3001\u30a8\u30e9\u30fc\u30e9\u30c3\u30d4\u30f3\u30b0\u306f\u3001\u30a8\u30e9\u30fc\u306b\u8ffd\u52a0\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3092\u63d0\u4f9b\u3057\u3001\u30bd\u30fc\u30b9\u30a8\u30e9\u30fc\u3092\u8fd4\u3059\u3053\u3068\u304c\u3067\u304d\u308b\u305f\u3081\u3001\u89e3\u6c7a\u7b56\u306b\u306a\u308a\u307e\u3059\u3002

\u30a8\u30e9\u30fc\u3092\u8907\u6570\u56de\u51e6\u7406\u3059\u308b\u3053\u3068\u306f\u3001\u7279\u306bGo\u8a00\u8a9e\u306b\u9650\u3089\u305a\u3001\u958b\u767a\u8005\u304c\u983b\u7e41\u306b\u3084\u3063\u3066\u3057\u307e\u3046\u30df\u30b9\u3067\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u540c\u3058\u30a8\u30e9\u30fc\u304c\u8907\u6570\u56de\u30ed\u30b0\u306b\u8a18\u9332\u3055\u308c\u3001\u30c7\u30d0\u30c3\u30b0\u304c\u56f0\u96e3\u306b\u306a\u308b\u72b6\u6cc1\u304c\u767a\u751f\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002

\u30a8\u30e9\u30fc\u51e6\u7406\u306f 1 \u5ea6\u3067\u6e08\u307e\u3059\u3079\u304d\u3060\u3068\u3044\u3046\u3053\u3068\u3092\u899a\u3048\u3066\u304a\u304d\u307e\u3057\u3087\u3046\u3002\u30a8\u30e9\u30fc\u3092\u30ed\u30b0\u306b\u8a18\u9332\u3059\u308b\u3053\u3068\u306f\u3001\u30a8\u30e9\u30fc\u3092\u51e6\u7406\u3059\u308b\u3053\u3068\u3067\u3059\u3002\u3064\u307e\u308a\u3001\u884c\u3046\u3079\u304d\u306f\u3001\u30ed\u30b0\u306b\u8a18\u9332\u3059\u308b\u304b\u3001\u30a8\u30e9\u30fc\u3092\u8fd4\u3059\u304b\u306e\u3069\u3061\u3089\u304b\u3060\u3068\u3044\u3046\u3053\u3068\u3067\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u30b3\u30fc\u30c9\u304c\u7c21\u7d20\u5316\u3055\u308c\u3001\u30a8\u30e9\u30fc\u306e\u72b6\u6cc1\u306b\u3064\u3044\u3066\u3088\u308a\u9069\u5207\u306a\u6d1e\u5bdf\u304c\u5f97\u3089\u308c\u307e\u3059\u3002\u30a8\u30e9\u30fc\u30e9\u30c3\u30d4\u30f3\u30b0\u306f\u3001\u30bd\u30fc\u30b9\u30a8\u30e9\u30fc\u3092\u4f1d\u3048\u3001\u30a8\u30e9\u30fc\u306b\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3092\u8ffd\u52a0\u3067\u304d\u308b\u305f\u3081\u3001\u6700\u3082\u4f7f\u3044\u52dd\u624b\u306e\u826f\u3044\u624b\u6bb5\u306b\u306a\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#53","title":"\u30a8\u30e9\u30fc\u51e6\u7406\u3092\u3057\u306a\u3044 (#53)","text":"\u8981\u7d04

\u95a2\u6570\u547c\u3073\u51fa\u3057\u4e2d\u3067\u3042\u3063\u3066\u3082\u3001defer \u95a2\u6570\u5185\u3067\u3042\u3063\u3066\u3082\u3001\u30a8\u30e9\u30fc\u3092\u7121\u8996\u3059\u308b\u3068\u304d\u306f\u3001\u30d6\u30e9\u30f3\u30af\u8b58\u5225\u5b50\u3092\u4f7f\u7528\u3057\u3066\u660e\u78ba\u306b\u884c\u3046\u3079\u304d\u3067\u3059\u3002\u305d\u3046\u3057\u306a\u3044\u3068\u3001\u5c06\u6765\u306e\u8aad\u307f\u624b\u304c\u305d\u308c\u304c\u610f\u56f3\u7684\u3060\u3063\u305f\u306e\u304b\u3001\u305d\u308c\u3068\u3082\u30df\u30b9\u3060\u3063\u305f\u306e\u304b\u56f0\u60d1\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#defer-54","title":"defer \u30a8\u30e9\u30fc\u3092\u51e6\u7406\u3057\u306a\u3044 (#54)","text":"\u8981\u7d04

\u591a\u304f\u306e\u5834\u5408\u3001defer \u95a2\u6570\u306b\u3088\u3063\u3066\u8fd4\u3055\u308c\u308b\u30a8\u30e9\u30fc\u3092\u7121\u8996\u3059\u3079\u304d\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u72b6\u6cc1\u306b\u5fdc\u3058\u3066\u3001\u76f4\u63a5\u51e6\u7406\u3059\u308b\u304b\u3001\u547c\u3073\u51fa\u3057\u5143\u306b\u4f1d\u3048\u307e\u3057\u3087\u3046\u3002\u3053\u308c\u3092\u7121\u8996\u3059\u308b\u5834\u5408\u306f\u3001\u30d6\u30e9\u30f3\u30af\u8b58\u5225\u5b50\u3092\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u6b21\u306e\u30b3\u30fc\u30c9\u3092\u8003\u3048\u3066\u307f\u307e\u3057\u3087\u3046\u3002

func f() {\n  // ...\n  notify() // \u30a8\u30e9\u30fc\u51e6\u7406\u306f\u7701\u7565\u3055\u308c\u3066\u3044\u307e\u3059\n}\n\nfunc notify() error {\n  // ...\n}\n

\u4fdd\u5b88\u6027\u306e\u89b3\u70b9\u304b\u3089\u3001\u3053\u306e\u30b3\u30fc\u30c9\u306f\u3044\u304f\u3064\u304b\u306e\u554f\u984c\u3092\u5f15\u304d\u8d77\u3053\u3059\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u3042\u308b\u4eba\u304c\u3053\u308c\u3092\u8aad\u3080\u3053\u3068\u3092\u8003\u3048\u3066\u307f\u307e\u3059\u3002\u8aad\u307f\u624b\u306f\u3001notify \u304c\u30a8\u30e9\u30fc\u3092\u8fd4\u3059\u306b\u3082\u304b\u304b\u308f\u3089\u305a\u3001\u305d\u306e\u30a8\u30e9\u30fc\u304c\u89aa\u95a2\u6570\u306b\u3088\u3063\u3066\u51e6\u7406\u3055\u308c\u306a\u3044\u3053\u3068\u306b\u6c17\u3065\u304d\u307e\u3059\u3002\u30a8\u30e9\u30fc\u51e6\u7406\u304c\u610f\u56f3\u7684\u3067\u3042\u308b\u304b\u3069\u3046\u304b\u3092\u679c\u305f\u3057\u3066\u63a8\u6e2c\u3067\u304d\u308b\u3067\u3057\u3087\u3046\u304b\u3002\u4ee5\u524d\u306e\u958b\u767a\u8005\u304c\u305d\u308c\u3092\u51e6\u7406\u3059\u308b\u306e\u3092\u5fd8\u308c\u305f\u306e\u304b\u3001\u305d\u308c\u3068\u3082\u610f\u56f3\u7684\u306b\u51e6\u7406\u3057\u305f\u306e\u304b\u3092\u77e5\u308b\u3053\u3068\u304c\u3067\u304d\u308b\u3067\u3057\u3087\u3046\u304b\u3002

\u3053\u308c\u3089\u306e\u7406\u7531\u306b\u3088\u308a\u3001\u30a8\u30e9\u30fc\u3092\u7121\u8996\u3057\u305f\u3044\u5834\u5408\u3001\u30d6\u30e9\u30f3\u30af\u8b58\u5225\u5b50\uff08 _ \uff09\u3092\u4f7f\u3046\u307b\u304b\u3042\u308a\u307e\u305b\u3093\u3002

_ = notify\n

\u30b3\u30f3\u30d1\u30a4\u30eb\u3068\u5b9f\u884c\u6642\u9593\u306e\u70b9\u3067\u306f\u3001\u3053\u306e\u65b9\u6cd5\u306f\u6700\u521d\u306e\u30b3\u30fc\u30c9\u90e8\u5206\u3068\u6bd4\u3079\u3066\u4f55\u3082\u5909\u308f\u308a\u307e\u305b\u3093\u3002\u3057\u304b\u3057\u3001\u3053\u306e\u65b0\u3057\u3044\u30d0\u30fc\u30b8\u30e7\u30f3\u3067\u306f\u3001\u79c1\u305f\u3061\u304c\u30a8\u30e9\u30fc\u306b\u95a2\u5fc3\u304c\u306a\u3044\u3053\u3068\u3092\u660e\u3089\u304b\u306b\u3057\u3066\u3044\u307e\u3059\u3002\u307e\u305f\u3001\u30a8\u30e9\u30fc\u304c\u7121\u8996\u3055\u308c\u308b\u7406\u7531\u3092\u793a\u3059\u30b3\u30e1\u30f3\u30c8\u3092\u8ffd\u52a0\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002

// \u6700\u5927\u3067\u3082 1 \u56de\u306e\u4f1d\u9054 \n// \u305d\u308c\u3086\u3048\u3001\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u305f\u5834\u5408\u306b\u305d\u308c\u3089\u306e\u4e00\u90e8\u304c\u5931\u308f\u308c\u308b\u3053\u3068\u306f\u8a31\u5bb9\u3055\u308c\u307e\u3059\n_ = notify()\n

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#_9","title":"\u4e26\u884c\u51e6\u7406\uff1a\u57fa\u790e","text":""},{"location":"ja/#55","title":"\u4e26\u884c\u51e6\u7406\u3068\u4e26\u5217\u51e6\u7406\u306e\u6df7\u540c (#55)","text":"\u8981\u7d04

\u4e26\u884c\u51e6\u7406\u3068\u4e26\u5217\u51e6\u7406\u306e\u57fa\u672c\u7684\u306a\u9055\u3044\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u306f\u3001 Go \u958b\u767a\u8005\u306b\u3068\u3063\u3066\u5fc5\u9808\u3067\u3059\u3002\u4e26\u884c\u51e6\u7406\u306f\u69cb\u9020\u306b\u95a2\u3059\u308b\u3082\u306e\u3067\u3059\u304c\u3001\u4e26\u5217\u51e6\u7406\u306f\u5b9f\u884c\u306b\u95a2\u3059\u308b\u3082\u306e\u3067\u3059\u3002

\u4e26\u884c\u51e6\u7406\u3068\u4e26\u5217\u51e6\u7406\u306f\u540c\u3058\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002

  • \u4e26\u884c\u51e6\u7406\u306f\u69cb\u9020\u306b\u95a2\u3059\u308b\u3082\u306e\u3067\u3059\u3002\u5225\u3005\u306e\u4e26\u884c\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u53d6\u308a\u7d44\u3080\u3053\u3068\u304c\u3067\u304d\u308b\u3055\u307e\u3056\u307e\u306a\u6bb5\u968e\u3092\u5c0e\u5165\u3059\u308b\u3053\u3068\u3067\u3001\u9010\u6b21\u51e6\u7406\u3092\u4e26\u884c\u51e6\u7406\u306b\u5909\u66f4\u3067\u304d\u307e\u3059\u3002
  • \u4e26\u5217\u51e6\u7406\u306f\u5b9f\u884c\u306b\u95a2\u3059\u308b\u3082\u306e\u3067\u3059\u3002\u4e26\u5217\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u3055\u3089\u306b\u8ffd\u52a0\u3059\u308b\u3053\u3068\u3067\u3001\u6bb5\u968e\u30ec\u30d9\u30eb\u3067\u4e26\u5217\u51e6\u7406\u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002

\u307e\u3068\u3081\u308b\u3068\u3001\u4e26\u884c\u51e6\u7406\u306f\u3001\u4e26\u5217\u5316\u3067\u304d\u308b\u90e8\u5206\u3092\u3082\u3064\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u305f\u3081\u306e\u69cb\u9020\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002\u3059\u306a\u308f\u3061\u3001\u4e26\u884c\u51e6\u7406\u306b\u3088\u308a\u4e26\u5217\u51e6\u7406\u304c\u53ef\u80fd \u306b\u306a\u308a\u307e\u3059 \u3002

"},{"location":"ja/#56","title":"\u4e26\u884c\u51e6\u7406\u306e\u307b\u3046\u304c\u5e38\u306b\u65e9\u3044\u3068\u8003\u3048\u3066\u3044\u308b (#56)","text":"\u8981\u7d04

\u719f\u7df4\u3057\u305f\u958b\u767a\u8005\u306b\u306a\u308b\u306b\u306f\u3001\u4e26\u884c\u51e6\u7406\u304c\u5fc5\u305a\u3057\u3082\u9ad8\u901f\u3067\u3042\u308b\u3068\u306f\u9650\u3089\u306a\u3044\u3053\u3068\u3092\u8a8d\u8b58\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u6700\u5c0f\u9650\u306e\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u306e\u4e26\u5217\u51e6\u7406\u3092\u4f34\u3046\u89e3\u6c7a\u7b56\u306f\u3001\u5fc5\u305a\u3057\u3082\u9010\u6b21\u51e6\u7406\u3088\u308a\u9ad8\u901f\u3067\u3042\u308b\u3068\u306f\u9650\u308a\u307e\u305b\u3093\u3002\u9010\u6b21\u51e6\u7406\u3068\u4e26\u884c\u51e6\u7406\u306e\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u306f\u3001\u4eee\u5b9a\u3092\u691c\u8a3c\u3059\u308b\u65b9\u6cd5\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u3002

\u30bb\u30af\u30b7\u30e7\u30f3\u5168\u6587\u306f\u3053\u3061\u3089\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#57","title":"\u30c1\u30e3\u30cd\u30eb\u307e\u305f\u306f\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u3092\u3044\u3064\u4f7f\u7528\u3059\u308b\u3079\u304d\u304b\u306b\u3064\u3044\u3066\u6238\u60d1\u3063\u3066\u3044\u308b (#57)","text":"\u8981\u7d04

\u30b4\u30eb\u30fc\u30c1\u30f3\u306e\u76f8\u4e92\u4f5c\u7528\u3092\u8a8d\u8b58\u3057\u3066\u3044\u308b\u3053\u3068\u306f\u3001\u30c1\u30e3\u30cd\u30eb\u3068\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u306e\u3069\u3061\u3089\u3092\u9078\u629e\u3059\u308b\u304b\u3092\u6c7a\u5b9a\u3059\u308b\u3068\u304d\u306b\u3082\u5f79\u7acb\u3061\u307e\u3059\u3002\u4e00\u822c\u306b\u3001\u4e26\u5217\u30b4\u30eb\u30fc\u30c1\u30f3\u306b\u306f\u540c\u671f\u304c\u5fc5\u8981\u3067\u3042\u308a\u3001\u3057\u305f\u304c\u3063\u3066\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u304c\u5fc5\u8981\u3067\u3059\u3002\u53cd\u5bfe\u306b\u3001\u4e26\u884c\u30b4\u30eb\u30fc\u30c1\u30f3\u306f\u901a\u5e38\u3001\u8abf\u6574\u3068\u30aa\u30fc\u30b1\u30b9\u30c8\u30ec\u30fc\u30b7\u30e7\u30f3\u3001\u3064\u307e\u308a\u30c1\u30e3\u30cd\u30eb\u3092\u5fc5\u8981\u3068\u3057\u307e\u3059\u3002

\u4e26\u884c\u51e6\u7406\u306e\u554f\u984c\u3092\u8003\u616e\u3059\u308b\u3068\u3001\u30c1\u30e3\u30cd\u30eb\u307e\u305f\u306f\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u3092\u4f7f\u7528\u3057\u305f\u89e3\u6c7a\u7b56\u3092\u5b9f\u88c5\u3067\u304d\u308b\u304b\u3069\u3046\u304b\u304c\u5fc5\u305a\u3057\u3082\u660e\u78ba\u3067\u306f\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002Go\u8a00\u8a9e\u306f\u901a\u4fe1\u306b\u3088\u308b\u30e1\u30e2\u30ea\u306e\u5171\u6709\u3092\u4fc3\u9032\u3059\u308b\u305f\u3081\u3001\u8d77\u3053\u308a\u3046\u308b\u9593\u9055\u3044\u306e\u3046\u3061\u306e\u4e00\u3064\u306f\u3001\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u306b\u304b\u304b\u308f\u3089\u305a\u3001\u30c1\u30e3\u30cd\u30eb\u306e\u4f7f\u7528\u3092\u5e38\u306b\u5f37\u5236\u3059\u308b\u3053\u3068\u3067\u3059\u3002\u3057\u304b\u3057\u306a\u304c\u3089\u30012 \u3064\u306e\u65b9\u6cd5\u306f\u88dc\u5b8c\u7684\u306a\u3082\u306e\u3067\u3042\u308b\u3068\u898b\u306a\u3059\u3079\u304d\u3067\u3059\u3002

\u30c1\u30e3\u30cd\u30eb\u307e\u305f\u306f\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u306f\u3069\u306e\u3088\u3046\u306a\u5834\u5408\u306b\u4f7f\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u306e\u3067\u3057\u3087\u3046\u304b\u3002\u6b21\u306e\u56f3\u306e\u4f8b\u3092\u30d0\u30c3\u30af\u30dc\u30fc\u30f3\u3068\u3057\u3066\u4f7f\u7528\u3057\u307e\u3059\u3002\u3053\u306e\u4f8b\u306b\u306f\u3001\u7279\u5b9a\u306e\u95a2\u4fc2\u3092\u6301\u3064 3 \u3064\u306e\u7570\u306a\u308b\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u3042\u308a\u307e\u3059\u3002

  • G1 \u3068 G2 \u306f\u4e26\u5217\u30b4\u30eb\u30fc\u30c1\u30f3\u3067\u3059\u3002\u30c1\u30e3\u30cd\u30eb\u304b\u3089\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u53d7\u4fe1\u3057\u7d9a\u3051\u308b\u540c\u3058\u95a2\u6570\u3092\u5b9f\u884c\u3059\u308b 2 \u3064\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u3001\u3042\u308b\u3044\u306f\u540c\u3058 HTTP \u30cf\u30f3\u30c9\u30e9\u3092\u540c\u6642\u306b\u5b9f\u884c\u3059\u308b 2 \u3064\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
  • G1 \u3068 G3 \u306f\u4e26\u884c\u30b4\u30eb\u30fc\u30c1\u30f3\u3067\u3042\u308a\u3001G2 \u3068 G3 \u3082\u540c\u69d8\u3067\u3059\u3002\u3059\u3079\u3066\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u306f\u5168\u4f53\u306e\u4e26\u884c\u69cb\u9020\u306e\u4e00\u90e8\u3067\u3059\u304c\u3001G1 \u3068 G2 \u304c\u6700\u521d\u306e\u30b9\u30c6\u30c3\u30d7\u3092\u5b9f\u884c\u3057\u3001G3 \u304c\u6b21\u306e\u30b9\u30c6\u30c3\u30d7\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002

\u539f\u5247\u3068\u3057\u3066\u3001\u4e26\u5217\u30b4\u30eb\u30fc\u30c1\u30f3\u306f\u3001\u30b9\u30e9\u30a4\u30b9\u306a\u3069\u306e\u5171\u6709\u30ea\u30bd\u30fc\u30b9\u306b\u30a2\u30af\u30bb\u30b9\u3057\u305f\u308a\u5909\u66f4\u3057\u305f\u308a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306a\u3069\u306b\u3001_\u540c\u671f_\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u540c\u671f\u306f\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u3067\u306f\u5f37\u5236\u3055\u308c\u307e\u3059\u304c\u3001\u3069\u306e\u30c1\u30e3\u30cd\u30eb\u578b\u3067\u3082\u5f37\u5236\u3055\u308c\u307e\u305b\u3093\uff08\u30d0\u30c3\u30d5\u30a1\u3042\u308a\u30c1\u30e3\u30cd\u30eb\u3092\u9664\u304f\uff09\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u4e00\u822c\u306b\u3001\u4e26\u5217\u30b4\u30eb\u30fc\u30c1\u30f3\u9593\u306e\u540c\u671f\u306f\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u3092\u4ecb\u3057\u3066\u9054\u6210\u3055\u308c\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

\u4e00\u65b9\u3001\u4e00\u822c\u306b\u3001\u4e26\u884c\u30b4\u30eb\u30fc\u30c1\u30f3\u306f \u8abf\u6574\u304a\u3088\u3073\u30aa\u30fc\u30b1\u30b9\u30c8\u30ec\u30fc\u30b7\u30e7\u30f3 \u3092\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001G3 \u304c G1 \u3068 G2 \u306e\u4e21\u65b9\u304b\u3089\u306e\u7d50\u679c\u3092\u96c6\u7d04\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u3001G1 \u3068 G2 \u306f\u65b0\u3057\u3044\u4e2d\u9593\u7d50\u679c\u304c\u5229\u7528\u53ef\u80fd\u3067\u3042\u308b\u3053\u3068\u3092 G3 \u306b\u901a\u77e5\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3053\u306e\u8abf\u6574\u306f\u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u7bc4\u56f2\u3001\u3064\u307e\u308a\u30c1\u30e3\u30cd\u30eb\u306b\u8a72\u5f53\u3057\u307e\u3059\u3002

\u4e26\u884c\u30b4\u30eb\u30fc\u30c1\u30f3\u306b\u95a2\u3057\u3066\u306f\u3001\u30ea\u30bd\u30fc\u30b9\u306e\u6240\u6709\u6a29\u3092\u3042\u308b\u30b9\u30c6\u30c3\u30d7\uff08G1 \u304a\u3088\u3073 G2\uff09\u304b\u3089\u5225\u306e\u30b9\u30c6\u30c3\u30d7\uff08G3\uff09\u306b\u79fb\u7ba1\u3057\u305f\u3044\u5834\u5408\u3082\u3042\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001G1 \u3068 G2 \u306b\u3088\u3063\u3066\u5171\u6709\u30ea\u30bd\u30fc\u30b9\u304c\u8c4a\u304b\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u3042\u308b\u6642\u70b9\u3067\u3053\u306e\u30b8\u30e7\u30d6\u306f\u5b8c\u4e86\u3057\u305f\u3068\u898b\u306a\u3055\u308c\u307e\u3059\u3002\u3053\u3053\u3067\u306f\u3001\u30c1\u30e3\u30cd\u30eb\u3092\u4f7f\u7528\u3057\u3066\u3001\u7279\u5b9a\u306e\u30ea\u30bd\u30fc\u30b9\u306e\u6e96\u5099\u304c\u3067\u304d\u3066\u3044\u308b\u3053\u3068\u3092\u901a\u77e5\u3057\u3001\u6240\u6709\u6a29\u306e\u79fb\u8ee2\u3092\u51e6\u7406\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u3068\u30c1\u30e3\u30cd\u30eb\u306b\u306f\u7570\u306a\u308b\u30bb\u30de\u30f3\u30c6\u30a3\u30af\u30b9\u304c\u3042\u308a\u307e\u3059\u3002\u30b9\u30c6\u30fc\u30c8\u3092\u5171\u6709\u3057\u305f\u3044\u3068\u304d\u3001\u307e\u305f\u306f\u5171\u6709\u30ea\u30bd\u30fc\u30b9\u306b\u30a2\u30af\u30bb\u30b9\u3057\u305f\u3044\u3068\u304d\u306f\u3001\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u306b\u3088\u3063\u3066\u3053\u306e\u30ea\u30bd\u30fc\u30b9\u3078\u306e\u6392\u4ed6\u7684\u30a2\u30af\u30bb\u30b9\u304c\u4fdd\u8a3c\u3055\u308c\u307e\u3059\u3002\u53cd\u5bfe\u306b\u3001\u30c1\u30e3\u30cd\u30eb\u306f\u30c7\u30fc\u30bf\u306e\u6709\u7121\uff08chan struct{} \u306e\u6709\u7121\uff09\u306b\u95a2\u4fc2\u306a\u304f\u30b7\u30b0\u30ca\u30eb\u3092\u884c\u3046\u4ed5\u7d44\u307f\u3067\u3059\u3002\u8abf\u6574\u3084\u6240\u6709\u6a29\u306e\u79fb\u8ee2\u306f\u30c1\u30e3\u30cd\u30eb\u3092\u901a\u3058\u3066\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u4e26\u5217\u304b\u4e26\u884c\u304b\u3092\u77e5\u308b\u3053\u3068\u304c\u91cd\u8981\u3067\u3059\u3002\u4e00\u822c\u306b\u3001\u4e26\u5217\u30b4\u30eb\u30fc\u30c1\u30f3\u306b\u306f\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u304c\u5fc5\u8981\u3067\u3001\u4e26\u884c\u30b4\u30eb\u30fc\u30c1\u30f3\u306b\u306f\u30c1\u30e3\u30cd\u30eb\u304c\u5fc5\u8981\u3067\u3059\u3002

"},{"location":"ja/#go-58","title":"\u7af6\u5408\u554f\u984c\u3092\u7406\u89e3\u3057\u3066\u3044\u306a\u3044\uff08\u30c7\u30fc\u30bf\u7af6\u5408\u3068\u7af6\u5408\u72b6\u614b\u3001\u305d\u3057\u3066Go\u8a00\u8a9e\u306e\u30e1\u30e2\u30ea\u30e2\u30c7\u30eb\uff09 (#58)","text":"\u8981\u7d04

\u4e26\u884c\u51e6\u7406\u306b\u719f\u9054\u3059\u308b\u3068\u3044\u3046\u3053\u3068\u306f\u3001\u30c7\u30fc\u30bf\u7af6\u5408\u3068\u7af6\u5408\u72b6\u614b\u304c\u7570\u306a\u308b\u6982\u5ff5\u3067\u3042\u308b\u3053\u3068\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u3082\u610f\u5473\u3057\u307e\u3059\u3002\u30c7\u30fc\u30bf\u7af6\u5408\u306f\u3001\u8907\u6570\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u540c\u3058\u30e1\u30e2\u30ea\u4f4d\u7f6e\u306b\u540c\u6642\u306b\u30a2\u30af\u30bb\u30b9\u3057\u3001\u305d\u306e\u3046\u3061\u306e\u5c11\u306a\u304f\u3068\u3082 1 \u3064\u304c\u66f8\u304d\u8fbc\u307f\u3092\u884c\u3063\u3066\u3044\u308b\u5834\u5408\u306b\u767a\u751f\u3057\u307e\u3059\u3002\u4e00\u65b9\u3001\u30c7\u30fc\u30bf\u7af6\u5408\u304c\u306a\u3044\u3053\u3068\u304c\u5fc5\u305a\u3057\u3082\u6c7a\u5b9a\u7684\u5b9f\u884c\u3092\u610f\u5473\u3059\u308b\u308f\u3051\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u52d5\u4f5c\u304c\u5236\u5fa1\u3067\u304d\u306a\u3044\u30a4\u30d9\u30f3\u30c8\u306e\u9806\u5e8f\u3084\u30bf\u30a4\u30df\u30f3\u30b0\u306b\u4f9d\u5b58\u3057\u3066\u3044\u308b\u5834\u5408\u3001\u3053\u308c\u306f\u7af6\u5408\u72b6\u614b\u3067\u3059\u3002

\u7af6\u5408\u554f\u984c\u306f\u3001\u30d7\u30ed\u30b0\u30e9\u30de\u30fc\u304c\u76f4\u9762\u3059\u308b\u53ef\u80fd\u6027\u306e\u3042\u308b\u30d0\u30b0\u306e\u4e2d\u3067\u6700\u3082\u56f0\u96e3\u304b\u3064\u6700\u3082\u6f5c\u4f0f\u6027\u306e\u9ad8\u3044\u30d0\u30b0\u306e 1 \u3064\u3068\u306a\u308a\u307e\u3059\u3002Go \u958b\u767a\u8005\u3068\u3057\u3066\u3001\u79c1\u305f\u3061\u306f\u30c7\u30fc\u30bf\u7af6\u5408\u3068\u7af6\u5408\u72b6\u614b\u3001\u305d\u308c\u3089\u304c\u53ca\u307c\u3057\u3046\u308b\u5f71\u97ff\u3001\u304a\u3088\u3073\u305d\u308c\u3089\u3092\u56de\u907f\u3059\u308b\u65b9\u6cd5\u306a\u3069\u306e\u91cd\u8981\u306a\u5074\u9762\u3092\u7406\u89e3\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

"},{"location":"ja/#_10","title":"\u30c7\u30fc\u30bf\u7af6\u5408","text":"

\u30c7\u30fc\u30bf\u7af6\u5408\u306f\u30012 \u3064\u4ee5\u4e0a\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u540c\u3058\u30e1\u30e2\u30ea\u4f4d\u7f6e\u306b\u540c\u6642\u306b\u30a2\u30af\u30bb\u30b9\u3057\u3001\u5c11\u306a\u304f\u3068\u3082 1 \u3064\u304c\u66f8\u304d\u8fbc\u307f\u3092\u884c\u3063\u3066\u3044\u308b\u5834\u5408\u306b\u767a\u751f\u3057\u307e\u3059\u3002\u3053\u306e\u5834\u5408\u3001\u5371\u967a\u306a\u7d50\u679c\u304c\u751f\u3058\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u3055\u3089\u306b\u60aa\u3044\u3053\u3068\u306b\u3001\u72b6\u6cc1\u306b\u3088\u3063\u3066\u306f\u3001\u30e1\u30e2\u30ea\u4f4d\u7f6e\u306b\u7121\u610f\u5473\u306a\u30d3\u30c3\u30c8\u306e\u7d44\u307f\u5408\u308f\u305b\u3092\u542b\u3080\u5024\u304c\u4fdd\u6301\u3055\u308c\u3066\u3057\u307e\u3046\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002

\u3055\u307e\u3056\u307e\u306a\u624b\u6cd5\u3092\u99c6\u4f7f\u3057\u3066\u3001\u30c7\u30fc\u30bf\u7af6\u5408\u306e\u767a\u751f\u3092\u9632\u3050\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u305f\u3068\u3048\u3070

  • sync/atomic \u30d1\u30c3\u30b1\u30fc\u30b8\u3092\u4f7f\u7528\u3059\u308b
  • 2 \u3064\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u540c\u671f\u3059\u308b\u969b\u306b\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u306e\u3088\u3046\u306a\u7279\u5b9a\u306e\u76ee\u7684\u306e\u305f\u3081\u306e\u30c7\u30fc\u30bf\u69cb\u9020\u3092\u5229\u7528\u3059\u308b
  • \u30c1\u30e3\u30cd\u30eb\u3092\u4f7f\u7528\u3057\u3066 2 \u3064\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u901a\u4fe1\u3057\u3001\u5909\u6570\u304c\u4e00\u5ea6\u306b 1 \u3064\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u3060\u3051\u306b\u3088\u3063\u3066\u66f4\u65b0\u3055\u308c\u308b\u3088\u3046\u306b\u3059\u308b
"},{"location":"ja/#_11","title":"\u7af6\u5408\u72b6\u614b","text":"

\u5b9f\u884c\u3057\u305f\u3044\u64cd\u4f5c\u306b\u5fdc\u3058\u3066\u3001\u30c7\u30fc\u30bf\u7af6\u5408\u306e\u306a\u3044\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u5fc5\u305a\u3057\u3082\u6c7a\u5b9a\u7684\u306a\u7d50\u679c\u3092\u610f\u5473\u3059\u308b\u3067\u3057\u3087\u3046\u304b\u3002\u305d\u3046\u3068\u306f\u3044\u3048\u307e\u305b\u3093\u3002

\u7af6\u5408\u72b6\u614b\u306f\u3001\u52d5\u4f5c\u304c\u5236\u5fa1\u3067\u304d\u306a\u3044\u30a4\u30d9\u30f3\u30c8\u306e\u30b7\u30fc\u30b1\u30f3\u30b9\u307e\u305f\u306f\u30bf\u30a4\u30df\u30f3\u30b0\u306b\u4f9d\u5b58\u3059\u308b\u5834\u5408\u306b\u767a\u751f\u3057\u307e\u3059\u3002\u3053\u3053\u3067\u306f\u3001\u30a4\u30d9\u30f3\u30c8\u306e\u30bf\u30a4\u30df\u30f3\u30b0\u304c\u30b4\u30eb\u30fc\u30c1\u30f3\u306e\u5b9f\u884c\u9806\u5e8f\u3067\u3059\u3002

\u307e\u3068\u3081\u308b\u3068\u3001\u4e26\u884c\u51e6\u7406\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u4f5c\u696d\u3059\u308b\u5834\u5408\u3001\u30c7\u30fc\u30bf\u7af6\u5408\u306f\u7af6\u5408\u72b6\u614b\u3068\u306f\u7570\u306a\u308b\u3053\u3068\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u304c\u4e0d\u53ef\u6b20\u3067\u3059\u3002\u30c7\u30fc\u30bf\u7af6\u5408\u306f\u3001\u8907\u6570\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u540c\u3058\u30e1\u30e2\u30ea\u4f4d\u7f6e\u306b\u540c\u6642\u306b\u30a2\u30af\u30bb\u30b9\u3057\u3001\u305d\u306e\u3046\u3061\u306e\u5c11\u306a\u304f\u3068\u3082 1 \u3064\u304c\u66f8\u304d\u8fbc\u307f\u3092\u884c\u3063\u3066\u3044\u308b\u5834\u5408\u306b\u767a\u751f\u3057\u307e\u3059\u3002\u30c7\u30fc\u30bf\u7af6\u5408\u3068\u306f\u3001\u4e88\u60f3\u5916\u306e\u52d5\u4f5c\u3092\u610f\u5473\u3057\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u30c7\u30fc\u30bf\u7af6\u5408\u306e\u306a\u3044\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u5fc5\u305a\u3057\u3082\u6c7a\u5b9a\u7684\u306a\u7d50\u679c\u3092\u610f\u5473\u3059\u308b\u308f\u3051\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u30c7\u30fc\u30bf\u7af6\u5408\u304c\u306a\u304f\u3066\u3082\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306f\u5236\u5fa1\u3055\u308c\u3066\u3044\u306a\u3044\u30a4\u30d9\u30f3\u30c8\uff08\u30b4\u30eb\u30fc\u30c1\u30f3\u306e\u5b9f\u884c\u3001\u30c1\u30e3\u30cd\u30eb\u3078\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u767a\u4fe1\u901f\u5ea6\u3001\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3078\u306e\u547c\u3073\u51fa\u3057\u306e\u7d99\u7d9a\u6642\u9593\u306a\u3069\uff09\u306b\u4f9d\u5b58\u3059\u308b\u6319\u52d5\u3092\u6301\u3064\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u306e\u5834\u5408\u306f\u7af6\u5408\u72b6\u614b\u3067\u3059\u3002\u4e26\u884c\u51e6\u7406\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u8a2d\u8a08\u306b\u719f\u7df4\u3059\u308b\u306b\u306f\u3001\u4e21\u65b9\u306e\u6982\u5ff5\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u304c\u809d\u8981\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#59","title":"\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u30bf\u30a4\u30d7\u3054\u3068\u306e\u4e26\u884c\u51e6\u7406\u306e\u5f71\u97ff\u3092\u7406\u89e3\u3057\u3066\u3044\u306a\u3044 (#59)","text":"\u8981\u7d04

\u4e00\u5b9a\u6570\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u4f5c\u6210\u3059\u308b\u3068\u304d\u306f\u3001\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u306e\u30bf\u30a4\u30d7\u3092\u8003\u616e\u3057\u3066\u304f\u3060\u3055\u3044\u3002CPU \u30d0\u30a6\u30f3\u30c9\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u4f5c\u6210\u3059\u308b\u3068\u3044\u3046\u3053\u3068\u306f\u3001\u3053\u306e\u6570\u3092 GOMAXPROCS \u5909\u6570\uff08\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u306f\u30db\u30b9\u30c8\u4e0a\u306e CPU \u30b3\u30a2\u306e\u6570\u306b\u57fa\u3065\u304f\uff09\u306b\u8fd1\u3065\u3051\u308b\u3053\u3068\u3092\u610f\u5473\u3057\u307e\u3059\u3002I/O \u30d0\u30a6\u30f3\u30c9\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u306e\u4f5c\u6210\u306f\u3001\u5916\u90e8\u30b7\u30b9\u30c6\u30e0\u306a\u3069\u306e\u4ed6\u306e\u8981\u56e0\u306b\u4f9d\u5b58\u3057\u307e\u3059\u3002

\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u3067\u306f\u3001\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u306e\u5b9f\u884c\u6642\u9593\u306f\u6b21\u306e\u3044\u305a\u308c\u304b\u306b\u3088\u3063\u3066\u5236\u9650\u3055\u308c\u307e\u3059\u3002

  • CPU \u306e\u901f\u5ea6 - \u305f\u3068\u3048\u3070\u3001\u30de\u30fc\u30b8\u30bd\u30fc\u30c8\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0\u306e\u5b9f\u884c\u304c\u3053\u308c\u306b\u3042\u305f\u308a\u307e\u3059\u3002\u3053\u306e\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u306f CPU \u30d0\u30a6\u30f3\u30c9\u3068\u547c\u3070\u308c\u307e\u3059\u3002
  • I/O \u306e\u901f\u5ea6 - \u305f\u3068\u3048\u3070\u3001REST \u547c\u3073\u51fa\u3057\u3084\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30af\u30a8\u30ea\u306e\u5b9f\u884c\u304c\u3053\u308c\u306b\u3042\u305f\u308a\u307e\u3059\u3002\u3053\u306e\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u306f I/O \u30d0\u30a6\u30f3\u30c9\u3068\u547c\u3070\u308c\u307e\u3059\u3002
  • \u5229\u7528\u53ef\u80fd\u306a\u30e1\u30e2\u30ea\u306e\u91cf - \u3053\u306e\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u306f\u30e1\u30e2\u30ea\u30d0\u30a6\u30f3\u30c9\u3068\u547c\u3070\u308c\u307e\u3059\u3002
\u88dc\u8db3

\u3053\u3053\u6570\u5341\u5e74\u3067\u30e1\u30e2\u30ea\u304c\u975e\u5e38\u306b\u5b89\u4fa1\u306b\u306a\u3063\u305f\u3053\u3068\u3092\u8003\u616e\u3059\u308b\u3068\u3001 3 \u3064\u76ee\u306f\u73fe\u5728\u3067\u306f\u6700\u3082\u307e\u308c\u3067\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u6700\u521d\u306e 2 \u3064\u306e\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u30bf\u30a4\u30d7\u3001CPU \u30d0\u30a6\u30f3\u30c9\u3068 I/O \u30d0\u30a6\u30f3\u30c9\u306b\u7126\u70b9\u3092\u5f53\u3066\u307e\u3059\u3002

\u30ef\u30fc\u30ab\u30fc\u306b\u3088\u3063\u3066\u5b9f\u884c\u3055\u308c\u308b\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u304c I/O \u30d0\u30a6\u30f3\u30c9\u3067\u3042\u308b\u5834\u5408\u3001\u5024\u306f\u4e3b\u306b\u5916\u90e8\u30b7\u30b9\u30c6\u30e0\u306b\u4f9d\u5b58\u3057\u307e\u3059\u3002\u9006\u306b\u3001\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u304c CPU \u306b\u4f9d\u5b58\u3057\u3066\u3044\u308b\u5834\u5408\u3001\u30b4\u30eb\u30fc\u30c1\u30f3\u306e\u6700\u9069\u306a\u6570\u306f\u5229\u7528\u53ef\u80fd\u306a CPU \u30b3\u30a2\u306e\u6570\u306b\u8fd1\u304f\u306a\u308a\u307e\u3059\uff08\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u306f runtime.GOMAXPROCS \u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3059\uff09\u3002\u4e26\u884c\u51e6\u7406\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u8a08\u3059\u308b\u5834\u5408\u3001\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u306e\u30bf\u30a4\u30d7\uff08 I/O \u3042\u308b\u3044\u306f CPU \uff09\u3092\u77e5\u308b\u3053\u3068\u304c\u91cd\u8981\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#go-context-60","title":"Go Context \u306b\u5bfe\u3059\u308b\u8aa4\u89e3 (#60)","text":"\u8981\u7d04

Go Context \u306f\u3001Go\u8a00\u8a9e\u306e\u4e26\u884c\u51e6\u7406\u306e\u57fa\u790e\u306e\u4e00\u90e8\u3067\u3082\u3042\u308a\u307e\u3059\u3002 Context \u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u30c7\u30c3\u30c9\u30e9\u30a4\u30f3\u3001\u30ad\u30fc\u3068\u5024\u306e\u30ea\u30b9\u30c8\u3092\u4fdd\u6301\u3067\u304d\u307e\u3059\u3002

https://pkg.go.dev/context

Context \u306f\u3001\u30c7\u30c3\u30c9\u30e9\u30a4\u30f3\u3001\u30ad\u30e3\u30f3\u30bb\u30eb\u30b7\u30b0\u30ca\u30eb\u3001\u305d\u306e\u4ed6\u306e\u5024\u3092 API \u306e\u5883\u754c\u3092\u8d8a\u3048\u3066\u4f1d\u9054\u3057\u307e\u3059\u3002

"},{"location":"ja/#_12","title":"\u30c7\u30c3\u30c9\u30e9\u30a4\u30f3","text":"

\u30c7\u30c3\u30c9\u30e9\u30a4\u30f3\u3068\u306f\u3001\u6b21\u306e\u3044\u305a\u308c\u304b\u3067\u6c7a\u5b9a\u3055\u308c\u308b\u7279\u5b9a\u306e\u6642\u70b9\u3092\u6307\u3057\u307e\u3059\u3002

  • \u73fe\u5728\u304b\u3089\u306e time.Duration \uff08\u4f8b\uff1a250 ms\uff09
  • time.Time \uff08\u4f8b\uff1a2023-02-07 00:00:00 UTC\uff09

\u30c7\u30c3\u30c9\u30e9\u30a4\u30f3\u306e\u30bb\u30de\u30f3\u30c6\u30a3\u30af\u30b9\u306f\u3001\u3053\u308c\u3092\u904e\u304e\u305f\u5834\u5408\u306f\u9032\u884c\u4e2d\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3\u3092\u505c\u6b62\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3053\u3068\u3092\u4f1d\u3048\u307e\u3059\u3002\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3\u3068\u306f\u3001\u305f\u3068\u3048\u3070\u3001\u30c1\u30e3\u30cd\u30eb\u304b\u3089\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u53d7\u4fe1\u3092\u5f85\u6a5f\u3057\u3066\u3044\u308b I/O \u30ea\u30af\u30a8\u30b9\u30c8\u3084\u30b4\u30eb\u30fc\u30c1\u30f3\u3067\u3059\u3002

"},{"location":"ja/#_13","title":"\u30ad\u30e3\u30f3\u30bb\u30eb\u30b7\u30b0\u30ca\u30eb","text":"

Go Context \u306e\u3082\u3046 1 \u3064\u306e\u4f7f\u7528\u4f8b\u306f\u3001\u30ad\u30e3\u30f3\u30bb\u30eb\u30b7\u30b0\u30ca\u30eb\u3092\u4f1d\u9001\u3059\u308b\u3053\u3068\u3067\u3059\u3002\u5225\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u5185\u3067 CreateFileWatcher(ctx context.Context, filename string) \u3092\u547c\u3073\u51fa\u3059\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u4f5c\u6210\u3059\u308b\u3053\u3068\u3092\u60f3\u50cf\u3057\u3066\u307f\u307e\u3057\u3087\u3046\u3002\u3053\u306e\u95a2\u6570\u306f\u3001\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u8aad\u307f\u53d6\u308a\u3092\u7d9a\u3051\u3066\u66f4\u65b0\u3092\u30ad\u30e3\u30c3\u30c1\u3059\u308b\u7279\u5b9a\u306e\u30d5\u30a1\u30a4\u30eb\u30a6\u30a9\u30c3\u30c1\u30e3\u30fc\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002\u63d0\u4f9b\u3055\u308c\u305f Context \u304c\u671f\u9650\u5207\u308c\u306b\u306a\u308b\u304b\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u308b\u3068\u3001\u3053\u306e\u95a2\u6570\u306f\u305d\u308c\u3092\u51e6\u7406\u3057\u3066\u30d5\u30a1\u30a4\u30eb\u8a18\u8ff0\u5b50\u3092\u9589\u3058\u307e\u3059\u3002

"},{"location":"ja/#context-value","title":"Context Value","text":"

Go Context \u306e\u6700\u5f8c\u306e\u4f7f\u7528\u4f8b\u306f\u3001\u30ad\u30fc\u3068\u5024\u306e\u30ea\u30b9\u30c8\u3092\u904b\u3076\u3053\u3068\u3067\u3059\u3002 Context \u306b\u30ad\u30fc\u3068\u5024\u306e\u30ea\u30b9\u30c8\u3092\u542b\u3081\u308b\u610f\u5473\u306f\u4f55\u3067\u3057\u3087\u3046\u304b\u3002Go Context \u306f\u6c4e\u7528\u7684\u3067\u3042\u308b\u305f\u3081\u3001\u4f7f\u7528\u4f8b\u306f\u7121\u9650\u306b\u3042\u308a\u307e\u3059\u3002

\u305f\u3068\u3048\u3070\u3001\u30c8\u30ec\u30fc\u30b9\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u3001\u7570\u306a\u308b\u30b5\u30d6\u95a2\u6570\u306e\u9593\u3067\u540c\u3058\u76f8\u95a2 ID \u3092\u5171\u6709\u3057\u305f\u3044\u3053\u3068\u304c\u3042\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002\u4e00\u90e8\u306e\u958b\u767a\u8005\u306f\u3001\u3053\u306e ID \u3092\u95a2\u6570\u30b7\u30b0\u30cd\u30c1\u30e3\u306e\u4e00\u90e8\u306b\u3059\u308b\u306b\u306f\u3042\u307e\u308a\u306b\u4fb5\u7565\u7684\u3060\u3068\u8003\u3048\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002\u3053\u306e\u70b9\u306b\u95a2\u3057\u3066\u3001\u4e0e\u3048\u3089\u308c\u305f Context \u306e\u4e00\u90e8\u3068\u3057\u3066\u305d\u308c\u3092\u542b\u3081\u308b\u3053\u3068\u3092\u6c7a\u5b9a\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002

"},{"location":"ja/#context","title":"Context \u306e\u30ad\u30e3\u30f3\u30bb\u30eb\u3092\u30ad\u30e3\u30c3\u30c1\u3059\u308b","text":"

context.Context \u30bf\u30a4\u30d7\u306f\u3001\u53d7\u4fe1\u5c02\u7528\u306e\u901a\u77e5\u30c1\u30e3\u30cd\u30eb <-chan struct{} \u3092\u8fd4\u3059 Done \u30e1\u30bd\u30c3\u30c9\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3057\u307e\u3059\u3002\u3053\u306e\u30c1\u30e3\u30cd\u30eb\u306f\u3001 Context \u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f\u4f5c\u696d\u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306b\u9589\u3058\u3089\u308c\u307e\u3059\u3002\u305f\u3068\u3048\u3070

  • context.WithCancel\u3067\u4f5c\u6210\u3055\u308c\u305f Context \u306b\u95a2\u9023\u3059\u308b Done \u30c1\u30e3\u30cd\u30eb\u306f\u3001cancel\u95a2\u6570\u304c\u547c\u3073\u51fa\u3055\u308c\u308b\u3068\u9589\u3058\u3089\u308c\u307e\u3059\u3002
  • context.WithDeadline\u3067\u4f5c\u6210\u3057\u305f Context \u306b\u95a2\u9023\u3059\u308b Done \u30c1\u30e3\u30cd\u30eb\u306f\u3001\u30c7\u30c3\u30c9\u30e9\u30a4\u30f3\u3092\u904e\u304e\u308b\u3068\u9589\u3058\u3089\u308c\u307e\u3059\u3002

\u6ce8\u610f\u3059\u3079\u304d\u70b9\u306e 1 \u3064\u306f\u3001\u5185\u90e8\u30c1\u30e3\u30cd\u30eb\u306f\u3001\u7279\u5b9a\u306e\u5024\u3092\u53d7\u3051\u53d6\u3063\u305f\u3068\u304d\u3067\u306f\u306a\u304f\u3001 Context \u304c\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u305f\u3068\u304d\u3001\u307e\u305f\u306f\u30c7\u30c3\u30c9\u30e9\u30a4\u30f3\u306b\u9054\u3057\u305f\u3068\u304d\u306b\u9589\u3058\u308b\u5fc5\u8981\u304c\u3042\u308b\u3068\u3044\u3046\u3053\u3068\u3067\u3059\u3002\u30c1\u30e3\u30cd\u30eb\u306e\u30af\u30ed\u30fc\u30ba\u306f\u3001\u3059\u3079\u3066\u306e\u6d88\u8cbb\u8005\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u53d7\u3051\u53d6\u308b\u552f\u4e00\u306e\u30c1\u30e3\u30cd\u30eb\u30a2\u30af\u30b7\u30e7\u30f3\u3067\u3042\u308b\u305f\u3081\u3067\u3059\u3002\u3053\u306e\u3088\u3046\u306b\u3057\u3066\u3001 Context \u304c\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u308b\u304b\u3001\u30c7\u30c3\u30c9\u30e9\u30a4\u30f3\u306b\u9054\u3059\u308b\u3068\u3001\u3059\u3079\u3066\u306e\u6d88\u8cbb\u8005\u306b\u901a\u77e5\u304c\u5c4a\u304d\u307e\u3059\u3002

\u307e\u3068\u3081\u308b\u3068\u3001\u719f\u7df4\u3057\u305f Go \u958b\u767a\u8005\u306b\u306a\u308b\u306b\u306f\u3001 Context \u3068\u305d\u306e\u4f7f\u7528\u65b9\u6cd5\u306b\u3064\u3044\u3066\u7406\u89e3\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u539f\u5247\u3068\u3057\u3066\u3001\u30e6\u30fc\u30b6\u30fc\u304c\u5f85\u6a5f\u3055\u305b\u3089\u308c\u308b\u95a2\u6570\u306f Context \u3092\u53d6\u5f97\u3059\u308b\u3079\u304d\u3067\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u4e0a\u6d41\u306e\u547c\u3073\u51fa\u3057\u5143\u304c\u3053\u306e\u95a2\u6570\u3092\u3044\u3064\u547c\u3073\u51fa\u3059\u304b\u3092\u6c7a\u5b9a\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308b\u304b\u3089\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#_14","title":"\u4e26\u884c\u51e6\u7406\uff1a\u5b9f\u8df5","text":""},{"location":"ja/#context-61","title":"\u4e0d\u9069\u5207\u306a Context \u3092\u5e83\u3081\u3066\u3057\u307e\u3046 (#61)","text":"\u8981\u7d04

Context \u3092\u4f1d\u64ad\u3059\u308b\u969b\u306b\u306f\u3001Context \u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u3067\u304d\u308b\u6761\u4ef6\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u304c\u91cd\u8981\u3067\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u9001\u4fe1\u3055\u308c\u305f\u969b\u306b HTTP \u30cf\u30f3\u30c9\u30e9\u304c Context \u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u3059\u308b\u3068\u304d\u306a\u3069\u3067\u3059\u3002

\u591a\u304f\u306e\u72b6\u6cc1\u3067\u306f\u3001Go Context \u3092\u4f1d\u64ad\u3059\u308b\u3053\u3068\u304c\u63a8\u5968\u3055\u308c\u307e\u3059\u3002\u305f\u3060\u3057\u3001Context \u306e\u4f1d\u64ad\u306b\u3088\u3063\u3066\u8efd\u5fae\u306a\u30d0\u30b0\u304c\u767a\u751f\u3057\u3001\u30b5\u30d6\u95a2\u6570\u304c\u6b63\u3057\u304f\u5b9f\u884c\u3055\u308c\u306a\u304f\u306a\u308b\u5834\u5408\u304c\u3042\u308a\u307e\u3059\u3002

\u6b21\u306e\u4f8b\u3092\u8003\u3048\u3066\u307f\u307e\u3057\u3087\u3046\u3002\u3044\u304f\u3064\u304b\u306e\u30bf\u30b9\u30af\u3092\u5b9f\u884c\u3057\u3066\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u8fd4\u3059 HTTP \u30cf\u30f3\u30c9\u30e9\u3092\u516c\u958b\u3057\u307e\u3059\u3002\u305f\u3060\u3057\u3001\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u8fd4\u3059\u76f4\u524d\u306b\u3001\u305d\u308c\u3092 Kafka \u30c8\u30d4\u30c3\u30af\u306b\u9001\u4fe1\u3057\u305f\u3044\u3068\u601d\u3063\u3066\u3044\u307e\u3059\u3002HTTP \u30b3\u30f3\u30b7\u30e5\u30fc\u30de\u306b\u30ec\u30a4\u30c6\u30f3\u30b7\u306e\u70b9\u3067\u30da\u30ca\u30eb\u30c6\u30a3\u3092\u8ab2\u3057\u305f\u304f\u306a\u3044\u306e\u3067\u3001publish \u30a2\u30af\u30b7\u30e7\u30f3\u3092\u65b0\u3057\u3044\u30b4\u30eb\u30fc\u30c1\u30f3\u5185\u3067\u975e\u540c\u671f\u306b\u51e6\u7406\u3057\u305f\u3044\u3068\u8003\u3048\u3066\u3044\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001Context \u304c\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u305f\u5834\u5408\u306b\u30e1\u30c3\u30bb\u30fc\u30b8\u306e publish \u30a2\u30af\u30b7\u30e7\u30f3\u3092\u4e2d\u65ad\u3067\u304d\u308b\u3088\u3046\u306b\u3001Context \u3092\u53d7\u3051\u5165\u308c\u308b publish \u95a2\u6570\u3092\u81ea\u7531\u306b\u4f7f\u3048\u308b\u3068\u3057\u307e\u3059\u3002\u53ef\u80fd\u306a\u5b9f\u88c5\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u3059\u3002

func handler(w http.ResponseWriter, r *http.Request) {\n    response, err := doSomeTask(r.Context(), r)\n    if err != nil {\n        http.Error(w, err.Error(), http.StatusInternalServerError)\n    return\n    }\n    go func() {\n        err := publish(r.Context(), response)\n        // err \u306e\u51e6\u7406\u3092\u3059\u308b\n    }()\n    writeResponse(response)\n}\n

\u3053\u306e\u30b3\u30fc\u30c9\u306e\u4f55\u304c\u554f\u984c\u306a\u306e\u3067\u3057\u3087\u3046\u304b\u3002HTTP \u30ea\u30af\u30a8\u30b9\u30c8\u306b\u4ed8\u3055\u308c\u305f Context \u306f\u3001\u3055\u307e\u3056\u307e\u306a\u72b6\u6cc1\u3067\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u3053\u3068\u3092\u77e5\u3063\u3066\u304a\u304f\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

  • \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u63a5\u7d9a\u304c\u7d42\u4e86\u3057\u305f\u3068\u304d
  • HTTP/2\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u5834\u5408\u3001\u30ea\u30af\u30a8\u30b9\u30c8\u304c\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u305f\u3068\u304d
  • \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u66f8\u304d\u623b\u3055\u308c\u305f\u3068\u304d

\u6700\u521d\u306e 2 \u3064\u306e\u30b1\u30fc\u30b9\u3067\u306f\u3001\u51e6\u7406\u306f\u304a\u305d\u3089\u304f\u6b63\u3057\u304f\u884c\u308f\u308c\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001doSomeTask \u304b\u3089\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u53d7\u3051\u53d6\u3063\u305f\u3082\u306e\u306e\u3001\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u63a5\u7d9a\u3092\u9589\u3058\u305f\u5834\u5408\u3001\u30e1\u30c3\u30bb\u30fc\u30b8\u304c publish \u3055\u308c\u306a\u3044\u3088\u3046\u306b\u3001Context \u304c\u65e2\u306b\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u305f\u72b6\u614b\u3067 publish \u3092\u547c\u3073\u51fa\u3057\u3066\u3082\u554f\u984c\u306f\u304a\u305d\u3089\u304f\u8d77\u304d\u307e\u305b\u3093\u3002\u3057\u304b\u3057\u3001\u6700\u5f8c\u306e\u30b1\u30fc\u30b9\u306f\u3069\u3046\u3067\u3057\u3087\u3046\u304b\u3002

\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b\u66f8\u304d\u8fbc\u307e\u308c\u308b\u3068\u3001\u8981\u6c42\u306b\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f Context \u304c\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u7af6\u5408\u72b6\u614b\u306b\u76f4\u9762\u3057\u307e\u3059\u3002

  • \u30ec\u30b9\u30dd\u30f3\u30b9\u304c Kafka \u306e publish \u5f8c\u306b\u66f8\u304b\u308c\u305f\u5834\u5408\u3001\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u8fd4\u3057\u3001\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u6b63\u5e38\u306b\u516c\u958b\u3057\u307e\u3059\u3002
  • \u305f\u3060\u3057\u3001Kafka \u306e publish \u524d\u307e\u305f\u306f publish \u4e2d\u306b\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u66f8\u304b\u308c\u305f\u5834\u5408\u3001\u30e1\u30c3\u30bb\u30fc\u30b8\u306f publish \u3055\u308c\u308b\u3079\u304d\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002

\u5f8c\u8005\u306e\u5834\u5408\u3001HTTP \u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u3059\u3050\u306b\u8fd4\u3059\u306e\u3067\u3001publish \u3092\u547c\u3073\u51fa\u3059\u3068\u30a8\u30e9\u30fc\u304c\u8fd4\u3055\u308c\u307e\u3059\u3002

\u88dc\u8db3

Go 1.21 \u304b\u3089\u306f\u3001\u30ad\u30e3\u30f3\u30bb\u30eb\u305b\u305a\u306b\u65b0\u3057\u3044 Context \u3092\u4f5c\u6210\u3059\u308b\u65b9\u6cd5\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002 context.WithoutCancel \u306f\u3001\u89aa\u304c\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u305f\u3068\u304d\u306b\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u3066\u3044\u306a\u3044\u89aa\u306e\u30b3\u30d4\u30fc\u3092\u8fd4\u3057\u307e\u3059\u3002

\u307e\u3068\u3081\u308b\u3068\u3001Context \u306e\u4f1d\u64ad\u306f\u614e\u91cd\u306b\u884c\u3046\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#62","title":"\u505c\u6b62\u3059\u3079\u304d\u3068\u304d\u3092\u77e5\u3089\u305a\u306b\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u958b\u59cb\u3057\u3066\u3057\u307e\u3046 (#62)","text":"\u8981\u7d04

\u30ea\u30fc\u30af\u3092\u907f\u3051\u308b\u3053\u3068\u306f\u3001\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u958b\u59cb\u3055\u308c\u308b\u305f\u3073\u306b\u3001\u6700\u7d42\u7684\u306b\u505c\u6b62\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3053\u3068\u3092\u610f\u5473\u3057\u307e\u3059\u3002

\u30b4\u30eb\u30fc\u30c1\u30f3\u306f\u7c21\u5358\u306b\u884c\u3046\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u975e\u5e38\u306b\u7c21\u5358\u3067\u3042\u308b\u305f\u3081\u3001\u65b0\u3057\u3044\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u3044\u3064\u505c\u6b62\u3059\u308b\u304b\u306b\u3064\u3044\u3066\u306e\u8a08\u753b\u3092\u5fc5\u305a\u3057\u3082\u7acb\u3066\u3066\u3044\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u3001\u30ea\u30fc\u30af\u306b\u3064\u306a\u304c\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u3044\u3064\u505c\u6b62\u3059\u308c\u3070\u3088\u3044\u304b\u308f\u304b\u3089\u306a\u3044\u306e\u306f\u3001Go\u8a00\u8a9e\u3067\u3088\u304f\u3042\u308b\u8a2d\u8a08\u4e0a\u306e\u554f\u984c\u3067\u3042\u308a\u3001\u4e26\u884c\u51e6\u7406\u306b\u304a\u3051\u308b\u30df\u30b9\u3067\u3059\u3002

\u5177\u4f53\u7684\u306a\u4f8b\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u307e\u3057\u3087\u3046\u3002\u5916\u90e8\u8a2d\u5b9a\uff08\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u306a\u3069\u3092\u4f7f\u7528\u3057\u305f\u3082\u306e\u306a\u3069\uff09\u3092\u76e3\u8996\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u8a08\u3057\u307e\u3059\u3002\u307e\u305a\u3001\u6b21\u306e\u3088\u3046\u306a\u5b9f\u88c5\u3092\u3057\u3066\u307f\u307e\u3059\u3002

func main() {\n    newWatcher()\n    // \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3059\u308b\n}\n\ntype watcher struct { /* \u3044\u304f\u3064\u304b\u306e\u30ea\u30bd\u30fc\u30b9 */ }\n\nfunc newWatcher() {\n    w := watcher{}\n    go w.watch() // \u5916\u90e8\u8a2d\u5b9a\u3092\u76e3\u8996\u3059\u308b\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u4f5c\u6210\u3059\u308b\n}\n

\u3053\u306e\u30b3\u30fc\u30c9\u306e\u554f\u984c\u306f\u3001\u30e1\u30a4\u30f3\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u7d42\u4e86\u3059\u308b\u3068\uff08\u304a\u305d\u3089\u304f OS \u30b7\u30b0\u30ca\u30eb\u307e\u305f\u306f\u6709\u9650\u306e\u30ef\u30fc\u30af\u30ed\u30fc\u30c9\u306e\u305f\u3081\uff09\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u505c\u6b62\u3057\u3066\u3057\u307e\u3046\u3053\u3068\u3067\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u30a6\u30a9\u30c3\u30c1\u30e3\u30fc\u306b\u3088\u3063\u3066\u4f5c\u6210\u3055\u308c\u305f\u30ea\u30bd\u30fc\u30b9\u306f\u6b63\u5e38\u306b\u9589\u3058\u3089\u308c\u307e\u305b\u3093\u3002\u3053\u308c\u3092\u9632\u3050\u306b\u306f\u3069\u3046\u3059\u308c\u3070\u3088\u3044\u3067\u3057\u3087\u3046\u304b\u3002

1 \u3064\u306e\u65b9\u6cd5\u3068\u3057\u3066\u306f\u3001main \u304c\u623b\u3063\u305f\u3068\u304d\u306b\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u308b Context \u3092 newWatcher \u306b\u6e21\u3059\u3053\u3068\u304c\u6319\u3052\u3089\u308c\u307e\u3059\u3002

func main() {\n    ctx, cancel := context.WithCancel(context.Background())\n    defer cancel()\n    newWatcher(ctx)\n    // \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3059\u308b\n}\n\nfunc newWatcher(ctx context.Context) {\n    w := watcher{}\n    go w.watch(ctx)\n}\n

\u4f5c\u6210\u3057\u305f Context \u3092 watch \u30e1\u30bd\u30c3\u30c9\u306b\u4f1d\u64ad\u3057\u307e\u3059\u3002Context \u304c\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u308b\u3068\u3001\u30a6\u30a9\u30c3\u30c1\u30e3\u30fc\u69cb\u9020\u4f53\u306f\u305d\u306e\u30ea\u30bd\u30fc\u30b9\u3092\u9589\u3058\u307e\u3059\u3002\u3057\u304b\u3057\u3001watch \u304c\u305d\u308c\u3092\u884c\u3046\u6642\u9593\u304c\u78ba\u5b9f\u306b\u3042\u308b\u3068\u306f\u3044\u3048\u307e\u305b\u3093\u3002\u3053\u308c\u306f\u8a2d\u8a08\u4e0a\u306e\u6b20\u9665\u3067\u3059\u3002

\u554f\u984c\u306f\u3001\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u505c\u6b62\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3053\u3068\u3092\u4f1d\u3048\u308b\u305f\u3081\u306b\u30b7\u30b0\u30ca\u30eb\u3092\u4f7f\u7528\u3057\u305f\u3053\u3068\u3067\u3059\u3002\u30ea\u30bd\u30fc\u30b9\u304c\u9589\u3058\u3089\u308c\u308b\u307e\u3067\u3001\u89aa\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u30d6\u30ed\u30c3\u30af\u3057\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u305d\u3046\u306a\u3089\u306a\u3044\u3088\u3046\u306b\u3057\u307e\u3057\u3087\u3046\u3002

func main() {\n    w := newWatcher()\n    defer w.close()\n    // \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3059\u308b\n}\n\nfunc newWatcher() watcher {\n    w := watcher{}\n    go w.watch()\n    return w\n}\n\nfunc (w watcher) close() {\n    // \u30ea\u30bd\u30fc\u30b9\u3092\u9589\u3058\u308b\n}\n

\u30ea\u30bd\u30fc\u30b9\u3092\u9589\u3058\u308b\u6642\u9593\u306b\u306a\u3063\u305f\u3053\u3068\u3092 watcher \u306b\u901a\u77e5\u3059\u308b\u4ee3\u308f\u308a\u306b\u3001 defer \u3092\u4f7f\u7528\u3057\u3066\u3053\u306e\u3000close \u30e1\u30bd\u30c3\u30c9\u3092\u547c\u3073\u51fa\u3057\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u7d42\u4e86\u3059\u308b\u524d\u306b\u30ea\u30bd\u30fc\u30b9\u304c\u78ba\u5b9f\u306b\u9589\u3058\u3089\u308c\u308b\u3088\u3046\u306b\u3057\u307e\u3059\u3002

\u307e\u3068\u3081\u308b\u3068\u3001\u30b4\u30eb\u30fc\u30c1\u30f3\u306f\u4ed6\u306e\u30ea\u30bd\u30fc\u30b9\u3068\u540c\u69d8\u3001\u30e1\u30e2\u30ea\u3084\u4ed6\u306e\u30ea\u30bd\u30fc\u30b9\u3092\u89e3\u653e\u3059\u308b\u305f\u3081\u306b\u6700\u7d42\u7684\u306b\u9589\u3058\u308b\u5fc5\u8981\u304c\u3042\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u3044\u3064\u505c\u6b62\u3059\u308b\u304b\u3092\u77e5\u3089\u305a\u306b\u958b\u59cb\u3059\u308b\u306e\u306f\u8a2d\u8a08\u4e0a\u306e\u554f\u984c\u3067\u3059\u3002\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u958b\u59cb\u3055\u308c\u308b\u3068\u304d\u306f\u5e38\u306b\u3001\u3044\u3064\u505c\u6b62\u3059\u308b\u304b\u306b\u3064\u3044\u3066\u660e\u78ba\u306a\u8a08\u753b\u3092\u7acb\u3066\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u6700\u5f8c\u306b\u306a\u308a\u307e\u3057\u305f\u304c\u3001\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u30ea\u30bd\u30fc\u30b9\u3092\u4f5c\u6210\u3057\u3001\u305d\u306e\u6709\u52b9\u671f\u9593\u304c\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u5b58\u7d9a\u671f\u9593\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u7d42\u4e86\u3059\u308b\u524d\u306b\u305d\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u304c\u5b8c\u4e86\u3059\u308b\u306e\u3092\u5f85\u3063\u305f\u65b9\u304c\u304a\u305d\u3089\u304f\u78ba\u5b9f\u3067\u3059\u3002\u305d\u3046\u3059\u308b\u3053\u3068\u3067\u3001\u30ea\u30bd\u30fc\u30b9\u3092\u9593\u9055\u3044\u306a\u304f\u89e3\u653e\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#63","title":"\u30b4\u30eb\u30fc\u30c1\u30f3\u3068\u30eb\u30fc\u30d7\u5909\u6570\u306b\u6ce8\u610f\u3057\u306a\u3044 (#63)","text":"\u6ce8\u610f

\u3053\u306e\u30df\u30b9\u306f Go\u30001.22 \u304b\u3089\u306f\u6c17\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u305b\u3093\uff08\u8a73\u7d30\uff09\u3002

"},{"location":"ja/#select-64","title":"select \u3068\u30c1\u30e3\u30cd\u30eb\u3092\u4f7f\u7528\u3057\u3066\u6c7a\u5b9a\u7684\u52d5\u4f5c\u3092\u671f\u5f85\u3059\u308b (#64)","text":"\u8981\u7d04

\u8907\u6570\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u304c\u53ef\u80fd\u306a\u5834\u5408\u3001\u8907\u6570\u306e\u30c1\u30e3\u30cd\u30eb\u3067 select \u3059\u308b\u3068\u30b1\u30fc\u30b9\u304c\u30e9\u30f3\u30c0\u30e0\u306b\u9078\u629e\u3055\u308c\u308b\u3053\u3068\u3092\u7406\u89e3\u3059\u308b\u3068\u3001\u4e26\u884c\u51e6\u7406\u306b\u304a\u3051\u308b\u8efd\u5fae\u306a\u30d0\u30b0\u306b\u3064\u306a\u304c\u308b\u53ef\u80fd\u6027\u306e\u3042\u308b\u8aa4\u3063\u305f\u4eee\u5b9a\u3092\u7acb\u3066\u308b\u3053\u3068\u304c\u306a\u304f\u306a\u308a\u307e\u3059\u3002

Go \u958b\u767a\u8005\u304c\u30c1\u30e3\u30cd\u30eb\u3092\u64cd\u4f5c\u3059\u308b\u3068\u304d\u306b\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044\u306e 1 \u3064\u306f\u3001select \u304c\u8907\u6570\u306e\u30c1\u30e3\u30cd\u30eb\u3067\u3069\u306e\u3088\u3046\u306b\u52d5\u4f5c\u3059\u308b\u304b\u306b\u3064\u3044\u3066\u8aa4\u3063\u305f\u7406\u89e3\u3092\u3059\u308b\u3053\u3068\u3067\u3059\u3002

\u305f\u3068\u3048\u3070\u3001\u6b21\u306e\u5834\u5408\u3092\u8003\u3048\u3066\u307f\u307e\u3057\u3087\u3046\uff08 disconnectCh \u306f\u30d0\u30c3\u30d5\u30a1\u306a\u3057\u30c1\u30e3\u30cd\u30eb\u3067\u3059\uff09\u3002

go func() {\n  for i := 0; i < 10; i++ {\n      messageCh <- i\n    }\n    disconnectCh <- struct{}{}\n}()\n\nfor {\n    select {\n    case v := <-messageCh:\n        fmt.Println(v)\n    case <-disconnectCh:\n        fmt.Println(\"disconnection, return\")\n        return\n    }\n}\n

\u3053\u306e\u4f8b\u3092\u8907\u6570\u56de\u5b9f\u884c\u3057\u305f\u5834\u5408\u3001\u7d50\u679c\u306f\u30e9\u30f3\u30c0\u30e0\u306b\u306a\u308a\u307e\u3059\u3002

0\n1\n2\ndisconnection, return\n\n0\ndisconnection, return\n

\u3069\u3046\u3044\u3046\u308f\u3051\u304b 10 \u901a\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u6d88\u8cbb\u3059\u308b\u306e\u3067\u306f\u306a\u304f\u3001\u305d\u306e\u3046\u3061\u306e\u6570\u901a\u3060\u3051\u3092\u53d7\u4fe1\u3057\u307e\u3057\u305f\u3002\u3053\u308c\u306f\u3001\u8907\u6570\u306e\u30c1\u30e3\u30cd\u30eb\u3068\u4f75\u7528\u3057\u305f\u5834\u5408\u306e select \u6587\u306e\u4ed5\u69d8\u306b\u3088\u308b\u3082\u306e\u3067\u3059\uff08https:// go.dev/ref/spec\uff09\u3002

Quote

1 \u3064\u4ee5\u4e0a\u306e\u901a\u4fe1\u3092\u7d9a\u884c\u3067\u304d\u308b\u5834\u5408\u3001\u5747\u4e00\u306e\u64ec\u4f3c\u30e9\u30f3\u30c0\u30e0\u9078\u629e\u306b\u3088\u3063\u3066\u3001\u7d9a\u884c\u3067\u304d\u308b 1 \u3064\u306e\u901a\u4fe1\u304c\u9078\u629e\u3055\u308c\u307e\u3059\u3002

\u6700\u521d\u306b\u4e00\u81f4\u3057\u305f\u30b1\u30fc\u30b9\u304c\u512a\u5148\u3055\u308c\u308b switch \u6587\u3068\u306f\u7570\u306a\u308a\u3001select \u6587\u306f\u8907\u6570\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u304c\u53ef\u80fd\u306a\u5834\u5408\u306b\u30e9\u30f3\u30c0\u30e0\u306b\u9078\u629e\u3057\u307e\u3059\u3002

\u3053\u306e\u52d5\u4f5c\u306f\u6700\u521d\u306f\u5947\u5999\u306b\u601d\u3048\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002\u3057\u304b\u3057\u3001\u3053\u308c\u306f\u30b9\u30bf\u30d9\u30fc\u30b7\u30e7\u30f3\u3092\u9632\u3050\u3068\u3044\u3046\u7406\u7531\u304c\u3042\u3063\u3066\u306e\u3053\u3068\u3067\u3059\u3002\u6700\u521d\u306b\u9078\u629e\u3055\u308c\u305f\u901a\u4fe1\u304c\u30bd\u30fc\u30b9\u306e\u9806\u5e8f\u306b\u57fa\u3065\u3044\u3066\u3044\u308b\u3068\u3057\u307e\u3059\u3002\u305d\u306e\u5834\u5408\u3001\u9001\u4fe1\u901f\u5ea6\u304c\u901f\u3044\u305f\u3081\u306b\u3001\u305f\u3068\u3048\u3070 1 \u3064\u306e\u30c1\u30e3\u30cd\u30eb\u304b\u3089\u3057\u304b\u53d7\u4fe1\u3067\u304d\u306a\u3044\u3068\u3044\u3046\u72b6\u6cc1\u306b\u9665\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u3053\u308c\u3092\u9632\u3050\u305f\u3081\u306b\u3001Go\u8a00\u8a9e\u306e\u8a2d\u8a08\u8005\u306f\u30e9\u30f3\u30c0\u30e0\u9078\u629e\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u306b\u3057\u307e\u3057\u305f\u3002

\u8907\u6570\u306e\u30c1\u30e3\u30cd\u30eb\u3067 select \u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u3001\u8907\u6570\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u304c\u3042\u308b\u306a\u3089\u3001\u30bd\u30fc\u30b9\u9806\u5e8f\u306e\u6700\u521d\u306e\u30b1\u30fc\u30b9\u304c\u81ea\u52d5\u7684\u306b\u512a\u5148\u3055\u308c\u308b\u308f\u3051\u3067\u306f\u306a\u3044\u3053\u3068\u306b\u6ce8\u610f\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u4ee3\u308f\u308a\u306b\u3001Go\u8a00\u8a9e\u306f\u30e9\u30f3\u30c0\u30e0\u306b\u9078\u629e\u3059\u308b\u305f\u3081\u3001\u3069\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u304c\u9078\u629e\u3055\u308c\u308b\u304b\u306f\u4fdd\u8a3c\u3055\u308c\u307e\u305b\u3093\u3002\u3053\u306e\u52d5\u4f5c\u3092\u514b\u670d\u3059\u308b\u306b\u306f\u3001\u5358\u4e00\u306e\u751f\u7523\u8005\u30b4\u30eb\u30fc\u30c1\u30f3\u306e\u5834\u5408\u3001\u30d0\u30c3\u30d5\u30a1\u306a\u3057\u306e\u30c1\u30e3\u30cd\u30eb\u307e\u305f\u306f\u5358\u4e00\u306e\u30c1\u30e3\u30cd\u30eb\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#65","title":"\u901a\u77e5\u30c1\u30e3\u30cd\u30eb\u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044 (#65)","text":"\u8981\u7d04

chan struct{} \u578b\u3092\u4f7f\u7528\u3057\u3066\u901a\u77e5\u3092\u9001\u4fe1\u3057\u307e\u3057\u3087\u3046\u3002

\u30c1\u30e3\u30cd\u30eb\u306f\u3001\u30b7\u30b0\u30ca\u30eb\u3092\u4ecb\u3057\u3066\u30b4\u30eb\u30fc\u30c1\u30f3\u9593\u3067\u901a\u4fe1\u3059\u308b\u305f\u3081\u306e\u30e1\u30ab\u30cb\u30ba\u30e0\u3067\u3059\u3002\u30b7\u30b0\u30ca\u30eb\u306b\u306f\u30c7\u30fc\u30bf\u304c\u542b\u307e\u308c\u3066\u3044\u308b\u304b\u3069\u3046\u304b\u306f\u95a2\u4fc2\u3042\u308a\u307e\u305b\u3093\u3002

\u5177\u4f53\u7684\u306a\u4f8b\u3092\u898b\u3066\u307f\u307e\u3057\u3087\u3046\u3002\u901a\u4fe1\u306e\u5207\u65ad\u304c\u767a\u751f\u3059\u308b\u305f\u3073\u306b\u305d\u308c\u3092\u901a\u77e5\u3059\u308b\u30c1\u30e3\u30cd\u30eb\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002 1 \u3064\u306e\u65b9\u6cd5\u3068\u3057\u3066\u3001\u3053\u308c\u3092 chan bool \u3068\u3057\u3066\u6271\u3046\u3053\u3068\u304c\u6319\u3052\u3089\u308c\u307e\u3059\u3002

disconnectCh := make(chan bool)\n

\u3053\u3053\u3067\u3001\u305d\u306e\u3088\u3046\u306a\u30c1\u30e3\u30cd\u30eb\u3092\u63d0\u4f9b\u3059\u308b API \u3068\u5bfe\u8a71\u3059\u308b\u3068\u3057\u307e\u3059\u3002\u3053\u308c\u306f\u771f\u507d\u5024\u306e\u30c1\u30e3\u30cd\u30eb\u3067\u3042\u308b\u305f\u3081\u3001true \u307e\u305f\u306f false \u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u53d7\u4fe1\u3067\u304d\u307e\u3059\u3002true \u304c\u4f55\u3092\u4f1d\u3048\u3066\u3044\u308b\u304b\u306f\u304a\u305d\u3089\u304f\u660e\u3089\u304b\u3067\u3057\u3087\u3046\u3002\u3057\u304b\u3057\u3001 false \u3068\u306f\u4f55\u3092\u610f\u5473\u3059\u308b\u306e\u3067\u3057\u3087\u3046\u304b\u3002\u901a\u4fe1\u304c\u5207\u65ad\u3055\u308c\u3066\u3044\u306a\u3044\u3068\u3044\u3046\u3053\u3068\u3067\u3057\u3087\u3046\u304b\u3002\u305d\u306e\u5834\u5408\u3001\u3069\u308c\u304f\u3089\u3044\u306e\u983b\u5ea6\u3067\u305d\u306e\u3088\u3046\u306a\u30b7\u30b0\u30ca\u30eb\u3092\u53d7\u4fe1\u3059\u308b\u306e\u3067\u3057\u3087\u3046\u304b\u3002\u3042\u308b\u3044\u306f\u518d\u63a5\u7d9a\u3057\u305f\u3068\u3044\u3046\u3053\u3068\u3067\u3057\u3087\u3046\u304b\u3002\u305d\u3082\u305d\u3082 false \u3092\u53d7\u3051\u53d6\u308b\u3053\u3068\u3092\u671f\u5f85\u3059\u3079\u304d\u306a\u306e\u3067\u3057\u3087\u3046\u304b\u3002\u304a\u305d\u3089\u304f true \u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u53d7\u3051\u53d6\u308b\u3053\u3068\u3060\u3051\u3092\u671f\u5f85\u3059\u3079\u304d\u3067\u3057\u3087\u3046\u3002

\u305d\u306e\u5834\u5408\u3001\u60c5\u5831\u3092\u4f1d\u3048\u308b\u305f\u3081\u306b\u7279\u5b9a\u306e\u5024\u306f\u5fc5\u8981\u306a\u3044\u3053\u3068\u3092\u610f\u5473\u3057\u3001\u30c7\u30fc\u30bf\u306e \u306a\u3044 \u30c1\u30e3\u30cd\u30eb\u304c\u5fc5\u8981\u306b\u306a\u308a\u307e\u3059\u3002\u3053\u308c\u3092\u51e6\u7406\u3059\u308b\u6163\u7528\u7684\u306a\u65b9\u6cd5\u306f\u3001\u7a7a\u306e\u69cb\u9020\u4f53\u306e\u30c1\u30e3\u30cd\u30eb\u2015\u2015 chan struct{}\u2015\u2015\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3059\u3002

"},{"location":"ja/#nil-66","title":"nil \u30c1\u30e3\u30cd\u30eb\u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044 (#66)","text":"\u8981\u7d04

nil \u30c1\u30e3\u30cd\u30eb\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u306b\u3088\u3063\u3066\u3001\u305f\u3068\u3048\u3070 select \u6587\u304b\u3089\u30b1\u30fc\u30b9\u3092 \u524a\u9664 \u3067\u304d\u308b\u305f\u3081\u3001\u4e26\u884c\u51e6\u7406\u3092\u884c\u3046\u969b\u306e\u9053\u5177\u306e\u4e00\u3064\u3068\u3057\u3066\u4f7f\u3048\u308b\u3088\u3046\u306b\u306a\u308b\u3079\u304d\u3067\u3059\u3002

\u6b21\u306e\u30b3\u30fc\u30c9\u306b\u3088\u3063\u3066\u4f55\u304c\u884c\u308f\u308c\u308b\u3067\u3057\u3087\u3046\u304b\u3002

var ch chan int\n<-ch\n

ch \u306f chan int \u578b\u3067\u3059\u3002\u30c1\u30e3\u30cd\u30eb\u306e\u30bc\u30ed\u5024\u306f nil \u3067\u3042\u308b\u306e\u3067\u3001 ch \u306f nil \u3067\u3059\u3002\u30b4\u30eb\u30fc\u30c1\u30f3\u306f panic \u3092\u8d77\u3053\u3057\u307e\u305b\u3093\u3002\u305f\u3060\u3057\u3001\u6c38\u4e45\u306b\u30d6\u30ed\u30c3\u30af\u3057\u307e\u3059\u3002

nil \u30c1\u30e3\u30cd\u30eb\u306b\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u9001\u4fe1\u3059\u308b\u5834\u5408\u3082\u539f\u7406\u306f\u540c\u3058\u3067\u3059\u3002\u4ee5\u4e0b\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u306f\u6c38\u4e45\u306b\u30d6\u30ed\u30c3\u30af\u3057\u307e\u3059\u3002

var ch chan int\nch <- 0\n

\u3067\u306f\u3001Go\u8a00\u8a9e\u304c nil \u30c1\u30e3\u30cd\u30eb\u3068\u306e\u9593\u3067\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u9001\u53d7\u4fe1\u3092\u8a31\u53ef\u3059\u308b\u76ee\u7684\u306f\u4f55\u3067\u3057\u3087\u3046\u304b\u3002\u305f\u3068\u3048\u3070\u30012 \u3064\u306e\u30c1\u30e3\u30cd\u30eb\u3092\u30de\u30fc\u30b8\u3059\u308b\u6163\u7528\u7684\u306a\u65b9\u6cd5\u3092\u5b9f\u88c5\u3059\u308b\u306e\u306b\u3001 nil \u30c1\u30e3\u30cd\u30eb\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

func merge(ch1, ch2 <-chan int) <-chan int {\n    ch := make(chan int, 1)\n\n    go func() {\n        for ch1 != nil || ch2 != nil { // \u6700\u4f4e\u3067\u3082\u4e00\u3064\u306e\u30c1\u30e3\u30cd\u30eb\u304c nil \u3067\u306a\u3051\u308c\u3070\u7d9a\u884c\u3059\u308b\n            select {\n            case v, open := <-ch1:\n                if !open {\n                    ch1 = nil // \u9589\u3058\u305f\u3089 ch1 \u3092 nil \u30c1\u30e3\u30cd\u30eb\u306b\u5272\u308a\u5f53\u3066\u308b\n                    break\n                }\n                ch <- v\n            case v, open := <-ch2:\n                if !open {\n                    ch2 = nil // \u9589\u3058\u305f\u3089 ch2 \u3092 nil \u30c1\u30e3\u30cd\u30eb\u306b\u5272\u308a\u5f53\u3066\u308b\n                    break\n                }\n                ch <- v\n            }\n        }\n        close(ch)\n    }()\n\n    return ch\n}\n

\u3053\u306e\u6d17\u7df4\u3055\u308c\u305f\u89e3\u6c7a\u7b56\u306f\u3001nil \u30c1\u30e3\u30cd\u30eb\u3092\u5229\u7528\u3057\u3066\u3001\u4f55\u3089\u304b\u306e\u65b9\u6cd5\u3067 select \u6587\u304b\u3089 1 \u3064\u306e\u30b1\u30fc\u30b9\u3092 \u524a\u9664 \u3057\u307e\u3059\u3002

nil \u30c1\u30e3\u30cd\u30eb\u306f\u72b6\u6cc1\u306b\u3088\u3063\u3066\u306f\u4fbf\u5229\u3067\u3042\u308a\u3001Go \u958b\u767a\u8005\u306f\u4e26\u884c\u51e6\u7406\u3092\u6271\u3046\u969b\u306b\u4f7f\u3044\u3053\u306a\u305b\u308b\u3088\u3046\u306b\u306a\u3063\u3066\u304a\u304f\u3079\u304d\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#67","title":"\u30c1\u30e3\u30cd\u30eb\u306e\u5bb9\u91cf\u306b\u3064\u3044\u3066\u56f0\u60d1\u3057\u3066\u3044\u308b (#67)","text":"\u8981\u7d04

\u554f\u984c\u304c\u767a\u751f\u3057\u305f\u5834\u5408\u306f\u3001\u4f7f\u7528\u3059\u308b\u30c1\u30e3\u30cd\u30eb\u306e\u578b\u3092\u614e\u91cd\u306b\u6c7a\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u540c\u671f\u3092\u5f37\u529b\u306b\u4fdd\u8a3c\u3057\u3066\u304f\u308c\u308b\u306e\u306f\u30d0\u30c3\u30d5\u30a1\u306a\u3057\u30c1\u30e3\u30cd\u30eb\u306e\u307f\u3067\u3059\u3002

\u30d0\u30c3\u30d5\u30a1\u3042\u308a\u30c1\u30e3\u30cd\u30eb\u4ee5\u5916\u306e\u30c1\u30e3\u30cd\u30eb\u306e\u5bb9\u91cf\u3092\u6307\u5b9a\u3059\u308b\u306b\u306f\u6b63\u5f53\u306a\u7406\u7531\u304c\u3042\u308b\u3079\u304d\u3067\u3059\u3002

"},{"location":"ja/#etcd-68","title":"\u6587\u5b57\u5217\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u8d77\u3053\u308a\u5f97\u308b\u526f\u4f5c\u7528\u3092\u5fd8\u308c\u3066\u3057\u307e\u3046\uff08 etcd \u30c7\u30fc\u30bf\u7af6\u5408\u306e\u4f8b\u3068\u30c7\u30c3\u30c9\u30ed\u30c3\u30af\uff09 (#68)","text":"\u8981\u7d04

\u6587\u5b57\u5217\u306e\u66f8\u5f0f\u8a2d\u5b9a\u304c\u65e2\u5b58\u306e\u95a2\u6570\u304c\u547c\u3073\u51fa\u3059\u53ef\u80fd\u6027\u304c\u3042\u308b\u3053\u3068\u3092\u8a8d\u8b58\u3059\u308b\u3053\u3068\u306f\u3001\u30c7\u30c3\u30c9\u30ed\u30c3\u30af\u3084\u305d\u306e\u4ed6\u306e\u30c7\u30fc\u30bf\u7af6\u5408\u306e\u53ef\u80fd\u6027\u306b\u6ce8\u610f\u3059\u308b\u3053\u3068\u3092\u610f\u5473\u3057\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#append-69","title":"append \u3067\u30c7\u30fc\u30bf\u7af6\u5408\u3092\u8d77\u3053\u3057\u3066\u3057\u307e\u3046 (#69)","text":"\u8981\u7d04

append \u306e\u547c\u3073\u51fa\u3057\u306f\u5fc5\u305a\u3057\u3082\u30c7\u30fc\u30bf\u7af6\u5408\u304c\u306a\u3044\u308f\u3051\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u3086\u3048\u306b\u3001\u5171\u6709\u30b9\u30e9\u30a4\u30b9\u4e0a\u3067\u540c\u6642\u306b\u4f7f\u7528\u3057\u3066\u306f\u3044\u3051\u307e\u305b\u3093\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#70","title":"\u30b9\u30e9\u30a4\u30b9\u3068\u30de\u30c3\u30d7\u3067\u30df\u30e5\u30fc\u30c6\u30c3\u30af\u30b9\u3092\u6b63\u3057\u304f\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044 (#70)","text":"\u8981\u7d04

\u30b9\u30e9\u30a4\u30b9\u3068\u30de\u30c3\u30d7\u306f\u30dd\u30a4\u30f3\u30bf\u3067\u3042\u308b\u3053\u3068\u3092\u899a\u3048\u3066\u304a\u304f\u3068\u3001\u5178\u578b\u7684\u306a\u30c7\u30fc\u30bf\u7af6\u5408\u3092\u9632\u3050\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#syncwaitgroup-71","title":"sync.WaitGroup \u3092\u6b63\u3057\u304f\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044 (#71)","text":"\u8981\u7d04

sync.WaitGroup \u3092\u6b63\u3057\u304f\u4f7f\u7528\u3059\u308b\u306b\u306f\u3001\u30b4\u30eb\u30fc\u30c1\u30f3\u3092\u8d77\u52d5\u3059\u308b\u524d\u306b Add \u30e1\u30bd\u30c3\u30c9\u3092\u547c\u3073\u51fa\u3057\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#synccond-72","title":"sync.Cond \u306b\u3064\u3044\u3066\u5fd8\u308c\u3066\u3057\u307e\u3046 (#72)","text":"\u8981\u7d04

sync.Cond \u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u8907\u6570\u306e\u30b4\u30eb\u30fc\u30c1\u30f3\u306b\u7e70\u308a\u8fd4\u3057\u901a\u77e5\u3092\u9001\u4fe1\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#errgroup-73","title":"errgroup \u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044 (#73)","text":"\u8981\u7d04

errgroup \u30d1\u30c3\u30b1\u30fc\u30b8\u3092\u4f7f\u7528\u3057\u3066\u3001\u30b4\u30eb\u30fc\u30c1\u30f3\u306e\u30b0\u30eb\u30fc\u30d7\u3092\u540c\u671f\u3057\u3001\u30a8\u30e9\u30fc\u3068 Context \u3092\u51e6\u7406\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#sync-74","title":"sync \u578b\u306e\u30b3\u30d4\u30fc (#74)","text":"\u8981\u7d04

sync \u578b\u306f\u30b3\u30d4\u30fc\u3055\u308c\u308b\u3079\u304d\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#_15","title":"\u6a19\u6e96\u30e9\u30a4\u30d6\u30e9\u30ea","text":""},{"location":"ja/#75","title":"\u9593\u9055\u3063\u305f\u6642\u9593\u3092\u6307\u5b9a\u3059\u308b (#75)","text":"\u8981\u7d04

time.Duration \u3092\u53d7\u3051\u5165\u308c\u308b\u95a2\u6570\u306b\u306f\u6ce8\u610f\u3092\u6255\u3063\u3066\u304f\u3060\u3055\u3044\u3002\u6574\u6570\u3092\u6e21\u3059\u3053\u3068\u306f\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u3059\u304c\u3001\u6df7\u4e71\u3092\u62db\u304b\u306a\u3044\u3088\u3046\u306b time API \u3092\u4f7f\u7528\u3059\u308b\u3088\u3046\u52aa\u3081\u3066\u304f\u3060\u3055\u3044\u3002

\u6a19\u6e96\u30e9\u30a4\u30d6\u30e9\u30ea\u306e\u591a\u304f\u306e\u95a2\u6570\u306f\u3001int64 \u578b\u306e\u30a8\u30a4\u30ea\u30a2\u30b9\u3067\u3042\u308b time.Duration \u3092\u53d7\u3051\u5165\u308c\u307e\u3059\u3002\u305f\u3060\u3057\u30011 \u5358\u4f4d\u306e time.Duration \u306f\u3001\u4ed6\u306e\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u8a00\u8a9e\u3067\u4e00\u822c\u7684\u306b\u898b\u3089\u308c\u308b 1 \u30df\u30ea\u79d2\u3067\u306f\u306a\u304f\u30011 \u30ca\u30ce\u79d2\u3092\u8868\u3057\u307e\u3059\u3002\u305d\u306e\u7d50\u679c\u3001time.Duration API \u3092\u4f7f\u7528\u3059\u308b\u4ee3\u308f\u308a\u306b\u6570\u5024\u578b\u3092\u6e21\u3059\u3068\u3001\u4e88\u60f3\u5916\u306e\u52d5\u4f5c\u304c\u767a\u751f\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002

\u4ed6\u8a00\u8a9e\u3092\u4f7f\u7528\u3057\u305f\u3053\u3068\u306e\u3042\u308b\u958b\u767a\u8005\u306e\u65b9\u306f\u3001\u6b21\u306e\u30b3\u30fc\u30c9\u306b\u3088\u3063\u3066 1 \u79d2\u5468\u671f\u306e\u65b0\u3057\u3044 time.Ticker \u304c\u751f\u6210\u3055\u308c\u308b\u3068\u8003\u3048\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002

ticker := time.NewTicker(1000)\nfor {\n    select {\n    case <-ticker.C:\n        // \u51e6\u7406\u3092\u3059\u308b\n    }\n}\n

\u3057\u304b\u3057\u306a\u304c\u3089\u30011,000 time.Duration = 1,000 \u30ca\u30ce\u79d2\u3067\u3042\u308b\u305f\u3081\u3001\u60f3\u5b9a\u3055\u308c\u3066\u3044\u308b 1\u79d2 \u3067\u306f\u306a\u304f\u30011,000 \u30ca\u30ce\u79d2 = 1 \u30de\u30a4\u30af\u30ed\u79d2\u306e\u5468\u671f\u306b\u306a\u308a\u307e\u3059\u3002

\u6df7\u4e71\u3084\u4e88\u60f3\u5916\u306e\u52d5\u4f5c\u3092\u62db\u304b\u306a\u3044\u3088\u3046\u3001\u3044\u3064\u3082 time.Duration API \u3092\u4f7f\u7528\u3059\u308b\u3079\u304d\u3067\u3059\u3002

ticker = time.NewTicker(time.Microsecond)\n// \u3082\u3057\u304f\u306f\nticker = time.NewTicker(1000 * time.Nanosecond)\n

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#timeafter-76","title":"time.After \u3068\u30e1\u30e2\u30ea\u30ea\u30fc\u30af (#76)","text":"\u8981\u7d04

\u7e70\u308a\u8fd4\u3055\u308c\u308b\u95a2\u6570\uff08\u30eb\u30fc\u30d7\u3084 HTTP \u30cf\u30f3\u30c9\u30e9\u306a\u3069\uff09\u3067 time.After \u306e\u547c\u3073\u51fa\u3057\u3092\u56de\u907f\u3059\u308b\u3068\u3001\u30d4\u30fc\u30af\u6642\u306e\u30e1\u30e2\u30ea\u6d88\u8cbb\u3092\u56de\u907f\u3067\u304d\u307e\u3059\u3002time.After \u306b\u3088\u3063\u3066\u751f\u6210\u3055\u308c\u305f\u30ea\u30bd\u30fc\u30b9\u306f\u3001 timer \u304c\u7d42\u4e86\u3057\u305f\u3068\u304d\u306b\u306e\u307f\u89e3\u653e\u3055\u308c\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#json-77","title":"JSON \u51e6\u7406\u3067\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044 (#77)","text":"
  • \u578b\u306e\u57cb\u3081\u8fbc\u307f\u306b\u3088\u308b\u4e88\u60f3\u5916\u306e\u52d5\u4f5c

Go \u69cb\u9020\u4f53\u3067\u57cb\u3081\u8fbc\u307f\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u306f\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002 \u306a\u305c\u306a\u3089 json.Marshaler \u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u5b9f\u88c5\u3059\u308b time.Time \u57cb\u3081\u8fbc\u307f\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u3088\u3046\u306a\u3084\u3063\u304b\u3044\u306a\u30d0\u30b0\u304c\u767a\u751f\u3057\u3066\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30de\u30fc\u30b7\u30e3\u30ea\u30f3\u30b0\u52d5\u4f5c\u304c\u30aa\u30fc\u30d0\u30fc\u30e9\u30a4\u30c9\u3055\u308c\u308b\u53ef\u80fd\u6027\u304c\u3042\u308b\u304b\u3089\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • JSON \u3068 monotonic clock

2 \u3064\u306e time.Time \u69cb\u9020\u4f53\u3092\u6bd4\u8f03\u3059\u308b\u5834\u5408\u3001time.Time \u306b\u306f wall clock \u3068 monotonic clock \u306e\u4e21\u65b9\u304c\u542b\u307e\u308c\u3066\u304a\u308a\u3001== \u6f14\u7b97\u5b50\u3092\u4f7f\u7528\u3057\u305f\u6bd4\u8f03\u306f\u4e21\u65b9\u306e clock \u306b\u5bfe\u3057\u3066\u884c\u308f\u308c\u308b\u3053\u3068\u3092\u601d\u3044\u51fa\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • any \u306e\u30de\u30c3\u30d7

JSON \u30c7\u30fc\u30bf\u306e\u30a2\u30f3\u30de\u30fc\u30b7\u30e3\u30ea\u30f3\u30b0\u4e2d\u306b\u30de\u30c3\u30d7\u3092\u63d0\u4f9b\u3059\u308b\u3068\u304d\u306b\u9593\u9055\u3044\u3092\u907f\u3051\u308b\u305f\u3081\u306b\u3001\u6570\u5024\u306f\u30c7\u30d5\u30a9\u30eb\u30c8\u3067 float64 \u306b\u5909\u63db\u3055\u308c\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#sql-78","title":"SQL \u3067\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044 (#78)","text":"
  • sql.Open \u304c\u5fc5\u305a\u3057\u3082\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3078\u306e\u63a5\u7d9a\u3092\u78ba\u7acb\u3059\u308b\u308f\u3051\u3067\u306f\u306a\u3044\u3053\u3068\u3092\u5fd8\u308c\u3066\u3044\u308b

\u8a2d\u5b9a\u3092\u8a66\u3057\u3001\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306f\u3001 Ping \u307e\u305f\u306f PingContext \u30e1\u30bd\u30c3\u30c9\u3092\u547c\u3073\u51fa\u3057\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • \u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u30d7\u30fc\u30ea\u30f3\u30b0\u306e\u3053\u3068\u3092\u5fd8\u308c\u308b

\u5b9f\u904b\u7528\u6c34\u6e96\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u30d1\u30e9\u30e1\u30fc\u30bf\u3092\u8a2d\u5b9a\u3057\u307e\u3057\u3087\u3046\u3002

  • \u30d7\u30ea\u30da\u30a2\u30c9\u30b9\u30c6\u30fc\u30c8\u30e1\u30f3\u30c8\u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044

SQL \u306e\u30d7\u30ea\u30da\u30a2\u30c9\u30b9\u30c6\u30fc\u30c8\u30e1\u30f3\u30c8\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u30af\u30a8\u30ea\u304c\u3088\u308a\u52b9\u7387\u7684\u304b\u3064\u78ba\u5b9f\u306b\u306a\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • null \u5024\u3092\u8aa4\u3063\u305f\u65b9\u6cd5\u3067\u51e6\u7406\u3057\u3066\u3044\u308b

\u30c6\u30fc\u30d6\u30eb\u5185\u306e null \u304c\u8a31\u5bb9\u3055\u308c\u3066\u3044\u308b\u5217\u306f\u3001\u30dd\u30a4\u30f3\u30bf\u307e\u305f\u306f sql.NullXXX \u578b\u3092\u4f7f\u7528\u3057\u3066\u51e6\u7406\u3057\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • \u884c\u306e\u53cd\u5fa9\u51e6\u7406\u306b\u3088\u308b\u30a8\u30e9\u30fc\u3092\u51e6\u7406\u3057\u306a\u3044

\u884c\u306e\u53cd\u5fa9\u51e6\u7406\u306e\u5f8c\u306b sql.Rows \u306e Err \u30e1\u30bd\u30c3\u30c9\u3092\u547c\u3073\u51fa\u3057\u3066\u3001\u6b21\u306e\u884c\u306e\u6e96\u5099\u4e2d\u306b\u30a8\u30e9\u30fc\u3092\u898b\u9003\u3057\u3066\u3044\u306a\u3044\u3053\u3068\u3092\u78ba\u8a8d\u3057\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#http-bodysqlrows-osfile-79","title":"\u4e00\u6642\u7684\u306a\u30ea\u30bd\u30fc\u30b9\uff08 HTTP body\u3001sql.Rows\u3001\u304a\u3088\u3073 os.File \uff09\u3092\u9589\u3058\u3066\u3044\u306a\u3044 (#79)","text":"\u8981\u7d04

\u30ea\u30fc\u30af\u3092\u907f\u3051\u308b\u305f\u3081\u306b\u3001 io.Closer \u3092\u5b9f\u88c5\u3057\u3066\u3044\u308b\u3059\u3079\u3066\u306e\u69cb\u9020\u4f53\u3092\u6700\u5f8c\u306b\u306f\u9589\u3058\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#http-return-80","title":"HTTP \u30ea\u30af\u30a8\u30b9\u30c8\u306b\u5fdc\u7b54\u3057\u305f\u5f8c\u306e return \u6587\u3092\u5fd8\u308c\u3066\u3057\u307e\u3046 (#80)","text":"\u8981\u7d04

HTTP \u30cf\u30f3\u30c9\u30e9\u306e\u5b9f\u88c5\u3067\u306e\u4e88\u60f3\u5916\u306e\u52d5\u4f5c\u3092\u907f\u3051\u308b\u305f\u3081\u3001http.Error \u306e\u5f8c\u306b\u30cf\u30f3\u30c9\u30e9\u3092\u505c\u6b62\u3057\u305f\u3044\u5834\u5408\u306f\u3001return \u6587\u3092\u5fd8\u308c\u306a\u3044\u3088\u3046\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#http-81","title":"\u6a19\u6e96\u306e HTTP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3068\u30b5\u30fc\u30d0\u30fc\u3092\u4f7f\u7528\u3057\u3066\u3044\u308b (#81)","text":"\u8981\u7d04

\u5b9f\u904b\u7528\u6c34\u6e96\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u6c42\u3081\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u6a19\u6e96\u306e HTTP \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3068\u30b5\u30fc\u30d0\u30fc\u306e\u5b9f\u88c5\u3092\u4f7f\u7528\u3057\u306a\u3044\u3067\u304f\u3060\u3055\u3044\u3002\u3053\u308c\u3089\u306e\u5b9f\u88c5\u306b\u306f\u3001\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3084\u7a3c\u50cd\u74b0\u5883\u3067\u5fc5\u9808\u3067\u3042\u308b\u3079\u304d\u52d5\u4f5c\u304c\u6b20\u843d\u3057\u3066\u3044\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#_16","title":"\u30c6\u30b9\u30c8","text":""},{"location":"ja/#82","title":"\u30c6\u30b9\u30c8\u3092\u5206\u985e\u3057\u3066\u3044\u306a\u3044\uff08\u30d3\u30eb\u30c9\u30bf\u30b0\u3001\u74b0\u5883\u5909\u6570\u3001\u30b7\u30e7\u30fc\u30c8\u30e2\u30fc\u30c9\uff09 (#82)","text":"\u8981\u7d04

\u30d3\u30eb\u30c9\u30d5\u30e9\u30b0\u3001\u74b0\u5883\u5909\u6570\u3001\u307e\u305f\u306f\u30b7\u30e7\u30fc\u30c8\u30e2\u30fc\u30c9\u3092\u4f7f\u7528\u3057\u3066\u30c6\u30b9\u30c8\u3092\u5206\u985e\u3059\u308b\u3068\u3001\u30c6\u30b9\u30c8\u30d7\u30ed\u30bb\u30b9\u304c\u3088\u308a\u52b9\u7387\u7684\u306b\u306a\u308a\u307e\u3059\u3002\u30d3\u30eb\u30c9\u30d5\u30e9\u30b0\u307e\u305f\u306f\u74b0\u5883\u5909\u6570\u3092\u4f7f\u7528\u3057\u3066\u30c6\u30b9\u30c8\u30ab\u30c6\u30b4\u30ea\uff08\u305f\u3068\u3048\u3070\u3001\u5358\u4f53\u30c6\u30b9\u30c8\u3068\u7d71\u5408\u30c6\u30b9\u30c8\uff09\u3092\u4f5c\u6210\u3057\u3001\u77ed\u671f\u9593\u306e\u30c6\u30b9\u30c8\u3068\u9577\u6642\u9593\u306e\u30c6\u30b9\u30c8\u3092\u533a\u5225\u3059\u308b\u3053\u3068\u3067\u3001\u5b9f\u884c\u3059\u308b\u30c6\u30b9\u30c8\u306e\u7a2e\u985e\u3092\u6c7a\u5b9a\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#-race-83","title":"-race \u30d5\u30e9\u30b0\u3092\u6709\u52b9\u306b\u3057\u3066\u3044\u306a\u3044 (#83)","text":"\u8981\u7d04

\u4e26\u884c\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u4f5c\u6210\u3059\u308b\u5834\u5408\u306f\u3001 -race \u30d5\u30e9\u30b0\u3092\u6709\u52b9\u306b\u3059\u308b\u3053\u3068\u3092\u5f37\u304f\u304a\u52e7\u3081\u3057\u307e\u3059\u3002\u305d\u3046\u3059\u308b\u3053\u3068\u3067\u3001\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u306e\u30d0\u30b0\u306b\u3064\u306a\u304c\u308b\u53ef\u80fd\u6027\u306e\u3042\u308b\u6f5c\u5728\u7684\u306a\u30c7\u30fc\u30bf\u7af6\u5408\u3092\u767a\u898b\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002

"},{"location":"ja/#-parallel-shuffle-84","title":"\u30c6\u30b9\u30c8\u5b9f\u884c\u30e2\u30fc\u30c9\uff08 -parallel \u304a\u3088\u3073 -shuffle \uff09\u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044 (#84)","text":"\u8981\u7d04

-parallel \u30d5\u30e9\u30b0\u3092\u4f7f\u7528\u3059\u308b\u306e\u306f\u3001\u7279\u306b\u9577\u6642\u9593\u5b9f\u884c\u3055\u308c\u308b\u30c6\u30b9\u30c8\u3092\u9ad8\u901f\u5316\u3059\u308b\u306e\u306b\u52b9\u679c\u7684\u3067\u3059\u3002 -shuffle \u30d5\u30e9\u30b0\u3092\u4f7f\u7528\u3059\u308b\u3068\u3001\u30c6\u30b9\u30c8\u30b9\u30a4\u30fc\u30c8\u304c\u30d0\u30b0\u3092\u96a0\u3059\u53ef\u80fd\u6027\u306e\u3042\u308b\u9593\u9055\u3063\u305f\u4eee\u5b9a\u306b\u4f9d\u5b58\u3057\u306a\u3044\u3088\u3046\u306b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

"},{"location":"ja/#85","title":"\u30c6\u30fc\u30d6\u30eb\u99c6\u52d5\u30c6\u30b9\u30c8\u3092\u4f7f\u7528\u3057\u306a\u3044 (#85)","text":"\u8981\u7d04

\u30c6\u30fc\u30d6\u30eb\u99c6\u52d5\u30c6\u30b9\u30c8\u306f\u3001\u30b3\u30fc\u30c9\u306e\u91cd\u8907\u3092\u9632\u304e\u3001\u5c06\u6765\u306e\u66f4\u65b0\u306e\u51e6\u7406\u3092\u5bb9\u6613\u306b\u3059\u308b\u305f\u3081\u306b\u3001\u4e00\u9023\u306e\u985e\u4f3c\u3057\u305f\u30c6\u30b9\u30c8\u3092\u30b0\u30eb\u30fc\u30d7\u5316\u3059\u308b\u52b9\u7387\u7684\u306a\u65b9\u6cd5\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#86","title":"\u5358\u4f53\u30c6\u30b9\u30c8\u3067\u306e\u30b9\u30ea\u30fc\u30d7 (#86)","text":"\u8981\u7d04

\u30c6\u30b9\u30c8\u306e\u4e0d\u5b89\u5b9a\u3055\u3092\u306a\u304f\u3057\u3001\u3088\u308a\u5805\u7262\u306b\u3059\u308b\u305f\u3081\u306b\u3001\u540c\u671f\u3092\u4f7f\u7528\u3057\u3066\u30b9\u30ea\u30fc\u30d7\u3092\u56de\u907f\u3057\u307e\u3057\u3087\u3046\u3002\u540c\u671f\u304c\u4e0d\u53ef\u80fd\u306a\u5834\u5408\u306f\u3001\u30ea\u30c8\u30e9\u30a4\u624b\u6cd5\u3092\u691c\u8a0e\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#time-api-87","title":"time API \u3092\u52b9\u7387\u7684\u306b\u51e6\u7406\u3067\u304d\u3066\u3044\u306a\u3044 (#87)","text":"\u8981\u7d04

time API \u3092\u4f7f\u7528\u3057\u3066\u95a2\u6570\u3092\u51e6\u7406\u3059\u308b\u65b9\u6cd5\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u3067\u3001\u30c6\u30b9\u30c8\u306e\u4e0d\u5b89\u5b9a\u3055\u3092\u8efd\u6e1b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u96a0\u308c\u305f\u4f9d\u5b58\u95a2\u4fc2\u306e\u4e00\u90e8\u3068\u3057\u3066 time \u3092\u51e6\u7406\u3057\u305f\u308a\u3001\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306b time \u3092\u63d0\u4f9b\u3059\u308b\u3088\u3046\u306b\u8981\u6c42\u3057\u305f\u308a\u3059\u308b\u306a\u3069\u3001\u6a19\u6e96\u7684\u306a\u624b\u6bb5\u3092\u5229\u7528\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#httptest-iotest-88","title":"\u30c6\u30b9\u30c8\u306b\u95a2\u3059\u308b\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u30d1\u30c3\u30b1\u30fc\u30b8\uff08 httptest \u304a\u3088\u3073 iotest \uff09\u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044 (#88)","text":"
  • httptest \u30d1\u30c3\u30b1\u30fc\u30b8\u306f\u3001HTTP \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u6271\u3046\u306e\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3068\u30b5\u30fc\u30d0\u30fc\u306e\u4e21\u65b9\u3092\u30c6\u30b9\u30c8\u3059\u308b\u305f\u3081\u306e\u4e00\u9023\u306e\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • iotest \u30d1\u30c3\u30b1\u30fc\u30b8\u306f\u3001io.Reader \u3092\u4f5c\u6210\u3057\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30a8\u30e9\u30fc\u8010\u6027\u3092\u30c6\u30b9\u30c8\u3059\u308b\u306e\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#89","title":"\u4e0d\u6b63\u78ba\u306a\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u306e\u4f5c\u6210 (#89)","text":"\u8981\u7d04

\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u306b\u3064\u3044\u3066

  • \u30d9\u30f3\u30c1\u30de\u30fc\u30af\u306e\u7cbe\u5ea6\u3092\u7dad\u6301\u3059\u308b\u306b\u306f\u3001time \u30e1\u30bd\u30c3\u30c9\u3092\u4f7f\u7528\u3057\u307e\u3057\u3087\u3046\u3002
  • \u30d9\u30f3\u30c1\u30bf\u30a4\u30e0\u3092\u5897\u3084\u3059\u304b\u3001benchstat \u306a\u3069\u306e\u30c4\u30fc\u30eb\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u30de\u30a4\u30af\u30ed\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u304c\u6271\u3044\u3084\u3059\u304f\u306a\u308a\u307e\u3059\u3002
  • \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u6700\u7d42\u7684\u306b\u5b9f\u884c\u3059\u308b\u30b7\u30b9\u30c6\u30e0\u304c\u30de\u30a4\u30af\u30ed\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u3092\u5b9f\u884c\u3059\u308b\u30b7\u30b9\u30c6\u30e0\u3068\u7570\u306a\u308b\u5834\u5408\u306f\u3001\u30de\u30a4\u30af\u30ed\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u306e\u7d50\u679c\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002
  • \u30b3\u30f3\u30d1\u30a4\u30e9\u306e\u6700\u9069\u5316\u306b\u3088\u3063\u3066\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u306e\u7d50\u679c\u304c\u8aa4\u9b54\u5316\u3055\u308c\u306a\u3044\u3088\u3046\u3001\u30c6\u30b9\u30c8\u5bfe\u8c61\u306e\u95a2\u6570\u304c\u526f\u4f5c\u7528\u3092\u5f15\u304d\u8d77\u3053\u3059\u3088\u3046\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002
  • \u30aa\u30d6\u30b6\u30fc\u30d0\u30fc\u52b9\u679c\u3092\u9632\u3050\u306b\u306f\u3001CPU \u306b\u4f9d\u5b58\u3059\u308b\u95a2\u6570\u304c\u4f7f\u7528\u3059\u308b\u30c7\u30fc\u30bf\u3092\u30d9\u30f3\u30c1\u30de\u30fc\u30af\u304c\u518d\u751f\u6210\u3059\u308b\u3088\u3046\u5f37\u5236\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30bb\u30af\u30b7\u30e7\u30f3\u5168\u6587\u306f\u3053\u3061\u3089\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#go-90","title":"Go\u8a00\u8a9e\u306e\u30c6\u30b9\u30c8\u6a5f\u80fd\u3092\u3059\u3079\u3066\u8a66\u3057\u3066\u3044\u306a\u3044 (#90)","text":"
  • \u30b3\u30fc\u30c9\u30ab\u30d0\u30ec\u30c3\u30b8

\u30b3\u30fc\u30c9\u306e\u3069\u306e\u90e8\u5206\u306b\u6ce8\u610f\u304c\u5fc5\u8981\u304b\u3092\u3059\u3050\u306b\u78ba\u8a8d\u3059\u308b\u305f\u3081\u306b\u3001-coverprofile \u30d5\u30e9\u30b0\u3092\u6307\u5b9a\u3057\u3066\u30b3\u30fc\u30c9\u30ab\u30d0\u30ec\u30c3\u30b8\u3092\u4f7f\u7528\u3057\u307e\u3057\u3087\u3046\u3002

  • \u5225\u306e\u30d1\u30c3\u30b1\u30fc\u30b8\u304b\u3089\u306e\u30c6\u30b9\u30c8

\u5185\u90e8\u3067\u306f\u306a\u304f\u516c\u958b\u3055\u308c\u305f\u52d5\u4f5c\u306b\u7126\u70b9\u3092\u5f53\u3066\u305f\u30c6\u30b9\u30c8\u306e\u4f5c\u6210\u3092\u5f37\u5236\u3059\u308b\u305f\u3081\u306b\u3001\u5358\u4f53\u30c6\u30b9\u30c8\u306f\u5225\u3005\u306e\u30d1\u30c3\u30b1\u30fc\u30b8\u306b\u914d\u7f6e\u3057\u307e\u3057\u3087\u3046\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • \u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u95a2\u6570

\u5f93\u6765\u306e if err != nil \u306e\u4ee3\u308f\u308a\u306b *testing.T \u5909\u6570\u3092\u4f7f\u7528\u3057\u3066\u30a8\u30e9\u30fc\u3092\u51e6\u7406\u3059\u308b\u3068\u3001\u30b3\u30fc\u30c9\u304c\u77ed\u304f\u3001\u8aad\u307f\u3084\u3059\u304f\u306a\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • setup \u3068 teardown

setup \u304a\u3088\u3073 teardown \u6a5f\u80fd\u3092\u5229\u7528\u3057\u3066\u3001\u7d71\u5408\u30c6\u30b9\u30c8\u306e\u5834\u5408\u306a\u3069\u3001\u8907\u96d1\u306a\u74b0\u5883\u3092\u69cb\u6210\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#community-mistake","title":"\u30d5\u30a1\u30b8\u30f3\u30b0\u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044\uff08community mistake\uff09","text":"\u8981\u7d04

\u30d5\u30a1\u30b8\u30f3\u30b0\u306f\u3001\u8907\u96d1\u306a\u95a2\u6570\u3084\u30e1\u30bd\u30c3\u30c9\u3078\u306e\u30e9\u30f3\u30c0\u30e0\u306a\u3001\u4e88\u60f3\u5916\u306e\u3001\u307e\u305f\u306f\u4e0d\u6b63\u306a\u5165\u529b\u3092\u691c\u51fa\u3057\u3001\u8106\u5f31\u6027\u3001\u30d0\u30b0\u3001\u3055\u3089\u306b\u306f\u6f5c\u5728\u7684\u306a\u30af\u30e9\u30c3\u30b7\u30e5\u3092\u767a\u898b\u3059\u308b\u306e\u306b\u52b9\u7387\u7684\u3067\u3059\u3002

@jeromedoucet \u3055\u3093\u306e\u3054\u5354\u529b\u306b\u611f\u8b1d\u3044\u305f\u3057\u307e\u3059\u3002

"},{"location":"ja/#_17","title":"\u6700\u9069\u5316","text":""},{"location":"ja/#cpu-91","title":"CPU \u30ad\u30e3\u30c3\u30b7\u30e5\u3092\u7406\u89e3\u3057\u3066\u3044\u306a\u3044 (#91)","text":"
  • CPU \u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3

L1 \u30ad\u30e3\u30c3\u30b7\u30e5\u306f\u30e1\u30a4\u30f3\u30e1\u30e2\u30ea\u3088\u308a\u3082\u7d04 50 \uff5e 100 \u500d\u9ad8\u901f\u3067\u3042\u308b\u305f\u3081\u3001CPU \u30d0\u30a6\u30f3\u30c9\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u6700\u9069\u5316\u3059\u308b\u306b\u306f\u3001CPU \u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u4f7f\u7528\u65b9\u6cd5\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u304c\u91cd\u8981\u3067\u3059\u3002

  • \u30ad\u30e3\u30c3\u30b7\u30e5\u30e9\u30a4\u30f3

\u30ad\u30e3\u30c3\u30b7\u30e5\u30e9\u30a4\u30f3\u306e\u6982\u5ff5\u3092\u610f\u8b58\u3059\u308b\u3053\u3068\u306f\u3001\u30c7\u30fc\u30bf\u96c6\u7d04\u578b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u30c7\u30fc\u30bf\u3092\u6574\u7406\u3059\u308b\u65b9\u6cd5\u3092\u7406\u89e3\u3059\u308b\u306e\u306b\u91cd\u8981\u3067\u3059\u3002CPU \u306f\u30e1\u30e2\u30ea\u3092\u30ef\u30fc\u30c9\u3054\u3068\u306b\u30d5\u30a7\u30c3\u30c1\u3057\u307e\u305b\u3093\u3002\u4ee3\u308f\u308a\u306b\u3001\u901a\u5e38\u306f\u30e1\u30e2\u30ea\u30d6\u30ed\u30c3\u30af\u3092 64 \u30d0\u30a4\u30c8\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u30e9\u30a4\u30f3\u306b\u30b3\u30d4\u30fc\u3057\u307e\u3059\u3002\u500b\u3005\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u30e9\u30a4\u30f3\u3092\u6700\u5927\u9650\u306b\u6d3b\u7528\u3059\u308b\u306b\u306f\u3001\u7a7a\u9593\u7684\u5c40\u6240\u6027\u3092\u5f37\u5236\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • \u69cb\u9020\u4f53\u306e\u30b9\u30e9\u30a4\u30b9\u3068\u30b9\u30e9\u30a4\u30b9\u306e\u69cb\u9020\u4f53

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • \u4e88\u6e2c\u53ef\u80fd\u6027

CPU \u306b\u3068\u3063\u3066\u4e88\u6e2c\u53ef\u80fd\u306a\u30b3\u30fc\u30c9\u306b\u3059\u308b\u3053\u3068\u306f\u3001\u7279\u5b9a\u306e\u95a2\u6570\u3092\u6700\u9069\u5316\u3059\u308b\u52b9\u7387\u7684\u306a\u65b9\u6cd5\u3067\u3082\u3042\u308a\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u30e6\u30cb\u30c3\u30c8\u307e\u305f\u306f\u5b9a\u6570\u30b9\u30c8\u30e9\u30a4\u30c9\u306f CPU \u306b\u3068\u3063\u3066\u4e88\u6e2c\u53ef\u80fd\u3067\u3059\u304c\u3001\u975e\u30e6\u30cb\u30c3\u30c8\u30b9\u30c8\u30e9\u30a4\u30c9\uff08\u9023\u7d50\u30ea\u30b9\u30c8\u306a\u3069\uff09\u306f\u4e88\u6e2c\u3067\u304d\u307e\u305b\u3093\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

  • \u30ad\u30e3\u30c3\u30b7\u30e5\u914d\u7f6e\u30dd\u30ea\u30b7\u30fc

\u30ad\u30e3\u30c3\u30b7\u30e5\u304c\u30d1\u30fc\u30c6\u30a3\u30b7\u30e7\u30f3\u5316\u3055\u308c\u3066\u3044\u308b\u3053\u3068\u3092\u8a8d\u8b58\u3059\u308b\u3053\u3068\u3067\u3001\u91cd\u5927\u306a\u30b9\u30c8\u30e9\u30a4\u30c9\u3092\u56de\u907f\u3057\u3001\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u3054\u304f\u4e00\u90e8\u306e\u307f\u3092\u4f7f\u7528\u3059\u308b\u3088\u3046\u306b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

"},{"location":"ja/#92","title":"\u8aa4\u3063\u305f\u5171\u6709\u3092\u5f15\u304d\u8d77\u3053\u3059\u4e26\u884c\u51e6\u7406(#92)","text":"\u8981\u7d04

\u4e0b\u4f4d\u30ec\u30d9\u30eb\u306e CPU \u30ad\u30e3\u30c3\u30b7\u30e5\u304c\u3059\u3079\u3066\u306e\u30b3\u30a2\u3067\u5171\u6709\u3055\u308c\u308b\u308f\u3051\u3067\u306f\u306a\u3044\u3053\u3068\u3092\u77e5\u3063\u3066\u304a\u304f\u3068\u3001\u4e26\u884c\u51e6\u7406\u306b\u304a\u3051\u308b\u306e\u8aa4\u3063\u305f\u5171\u6709\u306a\u3069\u3067\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u4f4e\u4e0b\u3055\u305b\u3066\u3057\u307e\u3046\u3053\u3068\u3092\u56de\u907f\u3067\u304d\u307e\u3059\u3002\u30e1\u30e2\u30ea\u306e\u5171\u6709\u306f\u3042\u308a\u3048\u306a\u3044\u306e\u3067\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#93","title":"\u547d\u4ee4\u30ec\u30d9\u30eb\u306e\u4e26\u5217\u6027\u3092\u8003\u616e\u3057\u306a\u3044 (#93)","text":"\u8981\u7d04

\u547d\u4ee4\u30ec\u30d9\u30eb\u306e\u4e26\u5217\u6027\uff08ILP\uff09\u3092\u4f7f\u7528\u3057\u3066\u30b3\u30fc\u30c9\u306e\u7279\u5b9a\u306e\u90e8\u5206\u3092\u6700\u9069\u5316\u3057\u3001CPU \u304c\u3067\u304d\u308b\u3060\u3051\u591a\u304f\u306e\u547d\u4ee4\u3092\u4e26\u5217\u5b9f\u884c\u3067\u304d\u308b\u3088\u3046\u306b\u3057\u307e\u3057\u3087\u3046\u3002\u4e3b\u306a\u624b\u9806\u306e 1 \u3064\u306b\u30c7\u30fc\u30bf\u30cf\u30b6\u30fc\u30c9\u306e\u7279\u5b9a\u304c\u3042\u308a\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#94","title":"\u30c7\u30fc\u30bf\u306e\u914d\u7f6e\u3092\u610f\u8b58\u3057\u3066\u3044\u306a\u3044 (#94)","text":"\u8981\u7d04

Go\u8a00\u8a9e\u3067\u306f\u3001\u57fa\u672c\u578b\u306f\u5404\u3005\u306e\u30b5\u30a4\u30ba\u306b\u5408\u308f\u305b\u3066\u914d\u7f6e\u3055\u308c\u308b\u3053\u3068\u3092\u899a\u3048\u3066\u304a\u304f\u3053\u3068\u3067\u3001\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044\u3092\u907f\u3051\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u69cb\u9020\u4f53\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u30b5\u30a4\u30ba\u3067\u964d\u9806\u306b\u518d\u7de8\u6210\u3059\u308b\u3068\u3001\u69cb\u9020\u4f53\u304c\u3088\u308a\u30b3\u30f3\u30d1\u30af\u30c8\u306b\u306a\u308b\uff08\u30e1\u30e2\u30ea\u5272\u308a\u5f53\u3066\u304c\u5c11\u306a\u304f\u306a\u308a\u3001\u7a7a\u9593\u7684\u5c40\u6240\u6027\u304c\u5411\u4e0a\u3059\u308b\uff09\u53ef\u80fd\u6027\u304c\u3042\u308b\u3053\u3068\u306b\u7559\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#95","title":"\u30d2\u30fc\u30d7\u3068\u30b9\u30bf\u30c3\u30af\u306e\u9055\u3044\u3092\u7406\u89e3\u3057\u3066\u3044\u306a\u3044 (#95)","text":"\u8981\u7d04

\u30d2\u30fc\u30d7\u3068\u30b9\u30bf\u30c3\u30af\u306e\u57fa\u672c\u7684\u306a\u9055\u3044\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u3082\u3001Go \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u6700\u9069\u5316\u3059\u308b\u969b\u306b\u306f\u5927\u5207\u3067\u3059\u3002\u30b9\u30bf\u30c3\u30af\u5272\u308a\u5f53\u3066\u306f\u5bb9\u6613\u306a\u306e\u306b\u5bfe\u3057\u3066\u3001\u30d2\u30fc\u30d7\u5272\u308a\u5f53\u3066\u306f\u9045\u304f\u3001\u30e1\u30e2\u30ea\u306e\u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7\u306b GC \u3092\u5229\u7528\u3057\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#api-syncpool-96","title":"\u5272\u308a\u5f53\u3066\u3092\u6e1b\u3089\u3059\u65b9\u6cd5\u304c\u308f\u304b\u3063\u3066\u3044\u306a\u3044\uff08 API \u306e\u5909\u66f4\u3001\u30b3\u30f3\u30d1\u30a4\u30e9\u306e\u6700\u9069\u5316\u3001\u304a\u3088\u3073 sync.Pool\uff09 (#96)","text":"\u8981\u7d04

\u5272\u308a\u5f53\u3066\u3092\u6e1b\u3089\u3059\u3053\u3068\u3082\u3001Go \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u6700\u9069\u5316\u3059\u308b\u4e0a\u3067\u91cd\u8981\u3067\u3059\u3002\u3053\u308c\u306f\u3001\u5171\u6709\u3092\u9632\u3050\u305f\u3081\u306b API \u3092\u614e\u91cd\u306b\u8a2d\u8a08\u3059\u308b\u3001\u4e00\u822c\u7684\u306a Go \u30b3\u30f3\u30d1\u30a4\u30e9\u306e\u6700\u9069\u5316\u3092\u7406\u89e3\u3059\u308b\u3001sync.Pool \u3092\u4f7f\u7528\u3059\u308b\u306a\u3069\u3001\u3055\u307e\u3056\u307e\u306a\u65b9\u6cd5\u3067\u884c\u3046\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002

\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9

"},{"location":"ja/#97","title":"\u30a4\u30f3\u30e9\u30a4\u30f3\u5c55\u958b\u3092\u3057\u3066\u3044\u306a\u3044 (#97)","text":"\u8981\u7d04

\u30d5\u30a1\u30b9\u30c8\u30d1\u30b9\u306e\u30a4\u30f3\u30e9\u30a4\u30f3\u5316\u624b\u6cd5\u3092\u4f7f\u7528\u3057\u3066\u3001\u95a2\u6570\u306e\u547c\u3073\u51fa\u3057\u306b\u304b\u304b\u308b\u511f\u5374\u6642\u9593\u3092\u52b9\u7387\u7684\u306b\u524a\u6e1b\u3057\u307e\u3057\u3087\u3046\u3002

"},{"location":"ja/#go-98","title":"Go\u8a00\u8a9e\u306e\u8a3a\u65ad\u30c4\u30fc\u30eb\u3092\u5229\u7528\u3057\u3066\u3044\u306a\u3044 (#98)","text":"\u8981\u7d04

\u30d7\u30ed\u30d5\u30a1\u30a4\u30ea\u30f3\u30b0\u3068\u5b9f\u884c\u30c8\u30ec\u30fc\u30b5\u3092\u5229\u7528\u3057\u3066\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3068\u6700\u9069\u5316\u3059\u3079\u304d\u90e8\u5206\u306b\u3064\u3044\u3066\u7406\u89e3\u3057\u307e\u3057\u3087\u3046\u3002

\u30bb\u30af\u30b7\u30e7\u30f3\u5168\u6587\u306f\u3053\u3061\u3089\u3002

"},{"location":"ja/#gc-99","title":"GC \u306e\u4ed5\u7d44\u307f\u3092\u7406\u89e3\u3057\u3066\u3044\u306a\u3044 (#99)","text":"\u8981\u7d04

GC \u306e\u8abf\u6574\u65b9\u6cd5\u3092\u7406\u89e3\u3059\u308b\u3068\u3001\u7a81\u7136\u306e\u8ca0\u8377\u306e\u5897\u52a0\u3092\u3088\u308a\u52b9\u7387\u7684\u306b\u51e6\u7406\u3067\u304d\u308b\u306a\u3069\u3001\u3055\u307e\u3056\u307e\u306a\u6069\u6075\u304c\u5f97\u3089\u308c\u307e\u3059\u3002

"},{"location":"ja/#docker-kubernetes-go-100","title":"Docker \u3068 Kubernetes \u4e0a\u3067Go\u8a00\u8a9e\u3092\u5b9f\u884c\u3059\u308b\u3053\u3068\u306e\u5f71\u97ff\u3092\u7406\u89e3\u3057\u3066\u3044\u306a\u3044 (#100)","text":"\u8981\u7d04

Docker \u3068 Kubernetes \u306b\u30c7\u30d7\u30ed\u30a4\u3059\u308b\u969b\u306e CPU \u30b9\u30ed\u30c3\u30c8\u30ea\u30f3\u30b0\u3092\u56de\u907f\u3059\u308b\u306b\u306f\u3001Go\u8a00\u8a9e\u304c CFS \u5bfe\u5fdc\u3067\u306f\u306a\u3044\u3053\u3068\u306b\u7559\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002

"},{"location":"zh/","title":"100\u4e2aGo\u5e38\u89c1\u9519\u8bef\u53ca\u5982\u4f55\u907f\u514d","text":"Jobs

Is your company hiring? Sponsor the Chinese version of this repository and let a significant audience of Go developers (~1k unique visitors per week) know about your opportunities in this section.

"},{"location":"zh/#_1","title":"\u4ee3\u7801\u53ca\u5de5\u7a0b\u7ec4\u7ec7","text":""},{"location":"zh/#1","title":"\u610f\u5916\u7684\u53d8\u91cf\u9690\u85cf (#1)","text":"

\u907f\u514d\u53d8\u91cf\u9690\u85cf\uff08\u5916\u90e8\u4f5c\u7528\u57df\u53d8\u91cf\u88ab\u5185\u90e8\u4f5c\u7528\u57df\u540c\u540d\u53d8\u91cf\u9690\u85cf\uff09\uff0c\u6709\u52a9\u4e8e\u907f\u514d\u53d8\u91cf\u5f15\u7528\u9519\u8bef\uff0c\u6709\u52a9\u4e8e\u4ed6\u4eba\u9605\u8bfb\u7406\u89e3\u3002

"},{"location":"zh/#2","title":"\u4e0d\u5fc5\u8981\u7684\u4ee3\u7801\u5d4c\u5957 (#2)","text":"

\u907f\u514d\u4e0d\u5fc5\u8981\u7684\u3001\u8fc7\u591a\u7684\u5d4c\u5957\u5c42\u6b21\uff0c\u5e76\u4e14\u8ba9\u6b63\u5e38\u4ee3\u7801\u8def\u5f84\u5c3d\u91cf\u5de6\u5bf9\u9f50\uff08\u800c\u4e0d\u662f\u653e\u5728\u5206\u652f\u8def\u5f84\u4e2d\uff09\uff0c\u6709\u52a9\u4e8e\u6784\u5efa\u53ef\u8bfb\u6027\u66f4\u597d\u7684\u4ee3\u7801\u3002

"},{"location":"zh/#init-3","title":"\u8bef\u7528init\u51fd\u6570 (#3)","text":"

\u521d\u59cb\u5316\u53d8\u91cf\u65f6\uff0c\u8bf7\u8bb0\u4f4f init \u51fd\u6570\u5177\u6709\u6709\u9650\u7684\u9519\u8bef\u5904\u7406\u80fd\u529b\uff0c\u5e76\u4e14\u4f1a\u4f7f\u72b6\u6001\u5904\u7406\u548c\u6d4b\u8bd5\u53d8\u5f97\u66f4\u52a0\u590d\u6742\u3002\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u521d\u59cb\u5316\u5e94\u8be5\u4f5c\u4e3a\u7279\u5b9a\u51fd\u6570\u6765\u5904\u7406\u3002

"},{"location":"zh/#getterssetters-4","title":"\u6ee5\u7528getters/setters (#4)","text":"

\u5728Go\u8bed\u8a00\u4e2d\uff0c\u5f3a\u5236\u4f7f\u7528getter\u548csetter\u65b9\u6cd5\u5e76\u4e0d\u7b26\u5408Go\u60ef\u4f8b\u3002\u5728\u5b9e\u8df5\u4e2d\uff0c\u5e94\u8be5\u627e\u5230\u6548\u7387\u548c\u76f2\u76ee\u9075\u5faa\u67d0\u4e9b\u60ef\u7528\u6cd5\u4e4b\u95f4\u7684\u5e73\u8861\u70b9\u3002

"},{"location":"zh/#5","title":"\u63a5\u53e3\u6c61\u67d3 (#5)","text":"

\u62bd\u8c61\u5e94\u8be5\u88ab\u53d1\u73b0\uff0c\u800c\u4e0d\u662f\u88ab\u521b\u9020\u3002\u4e3a\u4e86\u907f\u514d\u4e0d\u5fc5\u8981\u7684\u590d\u6742\u6027\uff0c\u9700\u8981\u65f6\u624d\u521b\u5efa\u63a5\u53e3\uff0c\u800c\u4e0d\u662f\u9884\u89c1\u5230\u9700\u8981\u5b83\uff0c\u6216\u8005\u81f3\u5c11\u53ef\u4ee5\u8bc1\u660e\u8fd9\u79cd\u62bd\u8c61\u662f\u6709\u4ef7\u503c\u7684\u3002

"},{"location":"zh/#6","title":"\u5c06\u63a5\u53e3\u5b9a\u4e49\u5728\u5b9e\u73b0\u65b9\u4e00\u4fa7 (#6)","text":"

\u5c06\u63a5\u53e3\u4fdd\u7559\u5728\u5f15\u7528\u65b9\u4e00\u4fa7\uff08\u800c\u4e0d\u662f\u5b9e\u73b0\u65b9\u4e00\u4fa7\uff09\u53ef\u4ee5\u907f\u514d\u4e0d\u5fc5\u8981\u7684\u62bd\u8c61\u3002

"},{"location":"zh/#7","title":"\u5c06\u63a5\u53e3\u4f5c\u4e3a\u8fd4\u56de\u503c (#7)","text":"

\u4e3a\u4e86\u907f\u514d\u5728\u7075\u6d3b\u6027\u65b9\u9762\u53d7\u5230\u9650\u5236\uff0c\u5927\u591a\u6570\u60c5\u51b5\u4e0b\u51fd\u6570\u4e0d\u5e94\u8be5\u8fd4\u56de\u63a5\u53e3\uff0c\u800c\u5e94\u8be5\u8fd4\u56de\u5177\u4f53\u7684\u5b9e\u73b0\u3002\u76f8\u53cd\uff0c\u51fd\u6570\u5e94\u8be5\u5c3d\u53ef\u80fd\u5730\u4f7f\u7528\u63a5\u53e3\u4f5c\u4e3a\u53c2\u6570\u3002

"},{"location":"zh/#any-8","title":"any \u6ca1\u4f20\u9012\u4efb\u4f55\u4fe1\u606f (#8)","text":"

\u53ea\u6709\u5728\u9700\u8981\u63a5\u53d7\u6216\u8fd4\u56de\u4efb\u610f\u7c7b\u578b\u65f6\uff0c\u624d\u4f7f\u7528 any\uff0c\u4f8b\u5982 json.Marshal\u3002\u5176\u4ed6\u60c5\u51b5\u4e0b\uff0c\u56e0\u4e3a any \u4e0d\u63d0\u4f9b\u6709\u610f\u4e49\u7684\u4fe1\u606f\uff0c\u53ef\u80fd\u4f1a\u5bfc\u81f4\u7f16\u8bd1\u65f6\u95ee\u9898\uff0c\u5982\u5141\u8bb8\u8c03\u7528\u8005\u8c03\u7528\u65b9\u6cd5\u5904\u7406\u4efb\u610f\u7c7b\u578b\u6570\u636e\u3002

"},{"location":"zh/#9","title":"\u56f0\u60d1\u4f55\u65f6\u8be5\u7528\u8303\u578b (#9)","text":"

\u4f7f\u7528\u6cdb\u578b\uff0c\u53ef\u4ee5\u901a\u8fc7\u7c7b\u578b\u53c2\u6570\u5206\u79bb\u5177\u4f53\u7684\u6570\u636e\u7c7b\u578b\u548c\u884c\u4e3a\uff0c\u907f\u514d\u5199\u5f88\u591a\u91cd\u590d\u5ea6\u5f88\u9ad8\u7684\u4ee3\u7801\u3002\u7136\u800c\uff0c\u4e0d\u8981\u8fc7\u65e9\u5730\u4f7f\u7528\u6cdb\u578b\u3001\u7c7b\u578b\u53c2\u6570\uff0c\u53ea\u6709\u5728\u4f60\u770b\u5230\u771f\u6b63\u9700\u8981\u65f6\u624d\u4f7f\u7528\u3002\u5426\u5219\uff0c\u5b83\u4eec\u4f1a\u5f15\u5165\u4e0d\u5fc5\u8981\u7684\u62bd\u8c61\u548c\u590d\u6742\u6027\u3002

"},{"location":"zh/#10","title":"\u672a\u610f\u8bc6\u5230\u7c7b\u578b\u5d4c\u5957\u7684\u53ef\u80fd\u95ee\u9898 (#10)","text":"

\u4f7f\u7528\u7c7b\u578b\u5d4c\u5957\u4e5f\u53ef\u4ee5\u907f\u514d\u5199\u4e00\u4e9b\u91cd\u590d\u4ee3\u7801\uff0c\u7136\u800c\uff0c\u5728\u4f7f\u7528\u65f6\u9700\u8981\u786e\u4fdd\u4e0d\u4f1a\u5bfc\u81f4\u4e0d\u5408\u7406\u7684\u53ef\u89c1\u6027\u95ee\u9898\uff0c\u6bd4\u5982\u6709\u4e9b\u5b57\u6bb5\u5e94\u8be5\u5bf9\u5916\u9690\u85cf\u4e0d\u5e94\u8be5\u88ab\u66b4\u6f0f\u3002

"},{"location":"zh/#function-option-11","title":"\u4e0d\u4f7f\u7528function option\u6a21\u5f0f (#11)","text":"

\u4e3a\u4e86\u8bbe\u8ba1\u5e76\u63d0\u4f9b\u66f4\u53cb\u597d\u7684API\uff08\u53ef\u9009\u53c2\u6570\uff09\uff0c\u4e3a\u4e86\u66f4\u597d\u5730\u5904\u7406\u9009\u9879\uff0c\u5e94\u8be5\u4f7f\u7528function option\u6a21\u5f0f\u3002

"},{"location":"zh/#12","title":"\u5de5\u7a0b\u7ec4\u7ec7\u4e0d\u5408\u7406 (\u5de5\u7a0b\u7ed3\u6784\u548c\u5305\u7684\u7ec4\u7ec7) (#12)","text":"

\u9075\u5faa\u50cf project-layout \u7684\u5efa\u8bae\u6765\u7ec4\u7ec7Go\u5de5\u7a0b\u662f\u4e00\u4e2a\u4e0d\u9519\u7684\u65b9\u6cd5\uff0c\u7279\u522b\u662f\u4f60\u6b63\u5728\u5bfb\u627e\u4e00\u4e9b\u7c7b\u4f3c\u7684\u7ecf\u9a8c\u3001\u60ef\u4f8b\u6765\u7ec4\u7ec7\u4e00\u4e2a\u65b0\u7684Go\u5de5\u7a0b\u7684\u65f6\u5019\u3002

"},{"location":"zh/#13","title":"\u521b\u5efa\u5de5\u5177\u5305 (#13)","text":"

\u547d\u540d\u662f\u8f6f\u4ef6\u8bbe\u8ba1\u5f00\u53d1\u4e2d\u975e\u5e38\u91cd\u8981\u7684\u4e00\u4e2a\u90e8\u5206\uff0c\u521b\u5efa\u4e00\u4e9b\u540d\u5982 common\u3001util\u3001shared \u4e4b\u7c7b\u7684\u5305\u540d\u5e76\u4e0d\u4f1a\u7ed9\u8bfb\u8005\u5e26\u6765\u592a\u5927\u4ef7\u503c\uff0c\u5e94\u8be5\u5c06\u8fd9\u4e9b\u5305\u540d\u91cd\u6784\u4e3a\u66f4\u6e05\u6670\u3001\u66f4\u805a\u7126\u7684\u5305\u540d\u3002

"},{"location":"zh/#14","title":"\u5ffd\u7565\u4e86\u5305\u540d\u51b2\u7a81 (#14)","text":"

\u4e3a\u4e86\u907f\u514d\u53d8\u91cf\u540d\u548c\u5305\u540d\u4e4b\u95f4\u7684\u51b2\u7a81\uff0c\u5bfc\u81f4\u6df7\u6dc6\u6216\u751a\u81f3\u9519\u8bef\uff0c\u5e94\u4e3a\u6bcf\u4e2a\u53d8\u91cf\u548c\u5305\u4f7f\u7528\u552f\u4e00\u7684\u540d\u79f0\u3002\u5982\u679c\u8fd9\u4e0d\u53ef\u884c\uff0c\u53ef\u4ee5\u8003\u8651\u4f7f\u7528\u5bfc\u5165\u522b\u540d import importAlias 'importPath'\uff0c\u4ee5\u533a\u5206\u5305\u540d\u548c\u53d8\u91cf\u540d\uff0c\u6216\u8005\u8003\u8651\u4e00\u4e2a\u66f4\u597d\u7684\u53d8\u91cf\u540d\u3002

"},{"location":"zh/#15","title":"\u4ee3\u7801\u7f3a\u5c11\u6587\u6863 (#15)","text":"

\u4e3a\u4e86\u8ba9\u4f7f\u7528\u65b9\u3001\u7ef4\u62a4\u4eba\u5458\u80fd\u66f4\u6e05\u6670\u5730\u4e86\u89e3\u4f60\u7684\u4ee3\u7801\u7684\u610f\u56fe\uff0c\u5bfc\u51fa\u7684\u5143\u7d20\uff08\u51fd\u6570\u3001\u7c7b\u578b\u3001\u5b57\u6bb5\uff09\u9700\u8981\u6dfb\u52a0godoc\u6ce8\u91ca\u3002

"},{"location":"zh/#linters-16","title":"\u4e0d\u4f7f\u7528linters\u68c0\u67e5 (#16)","text":"

\u4e3a\u4e86\u6539\u5584\u4ee3\u7801\u8d28\u91cf\u3001\u6574\u4f53\u4ee3\u7801\u7684\u4e00\u81f4\u6027\uff0c\u5e94\u8be5\u4f7f\u7528linters\u3001formatters\u3002

"},{"location":"zh/#_2","title":"\u6570\u636e\u7c7b\u578b","text":""},{"location":"zh/#17","title":"\u516b\u8fdb\u5236\u5b57\u9762\u91cf\u5f15\u53d1\u7684\u56f0\u60d1 (#17)","text":"

\u5728\u9605\u8bfb\u73b0\u6709\u4ee3\u7801\u65f6\uff0c\u8bf7\u8bb0\u4f4f\u4ee5 0 \u5f00\u5934\u7684\u6574\u6570\u5b57\u9762\u91cf\u662f\u516b\u8fdb\u5236\u6570\u3002\u6b64\u5916\uff0c\u4e3a\u4e86\u63d0\u9ad8\u53ef\u8bfb\u6027\uff0c\u53ef\u4ee5\u901a\u8fc7\u5728\u524d\u9762\u52a0\u4e0a 0o \u6765\u663e\u5f0f\u5730\u8868\u793a\u516b\u8fdb\u5236\u6574\u6570\u3002

"},{"location":"zh/#18","title":"\u672a\u6ce8\u610f\u53ef\u80fd\u7684\u6574\u6570\u6ea2\u51fa (#18)","text":"

\u5728 Go \u4e2d\u6574\u6570\u4e0a\u6ea2\u51fa\u548c\u4e0b\u6ea2\u662f\u9759\u9ed8\u5904\u7406\u7684\uff0c\u6240\u4ee5\u4f60\u53ef\u4ee5\u5b9e\u73b0\u81ea\u5df1\u7684\u51fd\u6570\u6765\u6355\u83b7\u5b83\u4eec\u3002

"},{"location":"zh/#19","title":"\u6ca1\u6709\u900f\u5f7b\u7406\u89e3\u6d6e\u70b9\u6570 (#19)","text":"

\u6bd4\u8f83\u6d6e\u70b9\u6570\u65f6\uff0c\u901a\u8fc7\u6bd4\u8f83\u4e8c\u8005\u7684delta\u503c\u662f\u5426\u4ecb\u4e8e\u4e00\u5b9a\u7684\u8303\u56f4\u5185\uff0c\u80fd\u8ba9\u4f60\u5199\u51fa\u53ef\u79fb\u690d\u6027\u66f4\u597d\u7684\u4ee3\u7801\u3002

\u5728\u8fdb\u884c\u52a0\u6cd5\u6216\u51cf\u6cd5\u65f6\uff0c\u5c06\u5177\u6709\u76f8\u4f3c\u6570\u91cf\u7ea7\u7684\u64cd\u4f5c\u5206\u6210\u540c\u4e00\u7ec4\u4ee5\u63d0\u9ad8\u7cbe\u5ea6 (\u8fc7\u65e9\u6307\u6570\u5bf9\u9f50\u4e22\u5931\u7cbe\u5ea6)\u3002\u6b64\u5916\uff0c\u5728\u8fdb\u884c\u52a0\u6cd5\u548c\u51cf\u6cd5\u4e4b\u524d\uff0c\u5e94\u5148\u8fdb\u884c\u4e58\u6cd5\u548c\u9664\u6cd5 (\u52a0\u51cf\u6cd5\u8bef\u5dee\u4f1a\u88ab\u4e58\u9664\u653e\u5927)\u3002

"},{"location":"zh/#slice-20","title":"\u4e0d\u7406\u89e3slice\u7684\u957f\u5ea6\u548c\u5bb9\u91cf (#20)","text":"

\u7406\u89e3slice\u7684\u957f\u5ea6\u548c\u5bb9\u91cf\u7684\u533a\u522b\uff0c\u662f\u4e00\u4e2aGo\u5f00\u53d1\u8005\u7684\u6838\u5fc3\u77e5\u8bc6\u70b9\u4e4b\u4e00\u3002slice\u7684\u957f\u5ea6\u6307\u7684\u662fslice\u5df2\u7ecf\u5b58\u50a8\u7684\u5143\u7d20\u7684\u6570\u91cf\uff0c\u800c\u5bb9\u91cf\u6307\u7684\u662fslice\u5f53\u524d\u5e95\u5c42\u5f00\u8f9f\u7684\u6570\u7ec4\u6700\u591a\u80fd\u5bb9\u7eb3\u7684\u5143\u7d20\u7684\u6570\u91cf\u3002

"},{"location":"zh/#slice-21","title":"\u4e0d\u9ad8\u6548\u7684slice\u521d\u59cb\u5316 (#21)","text":"

\u5f53\u521b\u5efa\u4e00\u4e2aslice\u65f6\uff0c\u5982\u679c\u5176\u957f\u5ea6\u53ef\u4ee5\u9884\u5148\u786e\u5b9a\uff0c\u90a3\u4e48\u53ef\u4ee5\u5728\u5b9a\u4e49\u65f6\u6307\u5b9a\u5b83\u7684\u957f\u5ea6\u548c\u5bb9\u91cf\u3002\u8fd9\u53ef\u4ee5\u6539\u5584\u540e\u671fappend\u65f6\u4e00\u6b21\u6216\u8005\u591a\u6b21\u7684\u5185\u5b58\u5206\u914d\u64cd\u4f5c\uff0c\u4ece\u800c\u6539\u5584\u6027\u80fd\u3002\u5bf9\u4e8emap\u7684\u521d\u59cb\u5316\u4e5f\u662f\u8fd9\u6837\u7684\u3002

"},{"location":"zh/#nilslice-22","title":"\u56f0\u60d1\u4e8enil\u548c\u7a7aslice (#22)","text":"

\u4e3a\u4e86\u907f\u514d\u5e38\u89c1\u7684\u5bf9nil\u548cempty slice\u5904\u7406\u884c\u4e3a\u7684\u6df7\u6dc6\uff0c\u4f8b\u5982\u5728\u4f7f\u7528 encoding/json \u6216 reflect \u5305\u65f6\uff0c\u4f60\u9700\u8981\u7406\u89e3 nil \u548c empty slice\u7684\u533a\u522b\u3002\u4e24\u8005\u90fd\u662f\u957f\u5ea6\u4e3a\u96f6\u3001\u5bb9\u91cf\u4e3a\u96f6\u7684\u5207\u7247\uff0c\u4f46\u662f nil \u5207\u7247\u4e0d\u9700\u8981\u5206\u914d\u5185\u5b58\u3002

"},{"location":"zh/#slice-23","title":"\u6ca1\u6709\u9002\u5f53\u68c0\u67e5slice\u662f\u5426\u4e3a\u7a7a (#23)","text":"

\u68c0\u67e5\u4e00\u4e2aslice\u7684\u662f\u5426\u5305\u542b\u4efb\u4f55\u5143\u7d20\uff0c\u53ef\u4ee5\u68c0\u67e5\u5176\u957f\u5ea6\uff0c\u4e0d\u7ba1slice\u662fnil\u8fd8\u662fempty\uff0c\u68c0\u67e5\u957f\u5ea6\u90fd\u662f\u6709\u6548\u7684\u3002\u8fd9\u4e2a\u68c0\u67e5\u65b9\u6cd5\u4e5f\u9002\u7528\u4e8emap\u3002

\u4e3a\u4e86\u8bbe\u8ba1\u66f4\u660e\u786e\u7684API\uff0cAPI\u4e0d\u5e94\u533a\u5206nil\u548c\u7a7a\u5207\u7247\u3002

"},{"location":"zh/#slice-24","title":"\u6ca1\u6709\u6b63\u786e\u62f7\u8d1dslice (#24)","text":"

\u4f7f\u7528 copy \u62f7\u8d1d\u4e00\u4e2aslice\u5143\u7d20\u5230\u53e6\u4e00\u4e2aslice\u65f6\uff0c\u9700\u8981\u8bb0\u5f97\uff0c\u5b9e\u9645\u62f7\u8d1d\u7684\u5143\u7d20\u6570\u91cf\u662f\u4e8c\u8005slice\u957f\u5ea6\u4e2d\u7684\u8f83\u5c0f\u503c\u3002

"},{"location":"zh/#slice-append-25","title":"slice append\u5e26\u6765\u7684\u9884\u671f\u4e4b\u5916\u7684\u526f\u4f5c\u7528 (#25)","text":"

\u5982\u679c\u4e24\u4e2a\u4e0d\u540c\u7684\u51fd\u6570\u64cd\u4f5c\u7684slice\u590d\u7528\u4e86\u76f8\u540c\u7684\u5e95\u5c42\u6570\u7ec4\uff0c\u5b83\u4eec\u5bf9slice\u6267\u884cappend\u64cd\u4f5c\u65f6\u53ef\u80fd\u4f1a\u4ea7\u751f\u51b2\u7a81\u3002\u4f7f\u7528copy\u6765\u5b8c\u6574\u590d\u5236\u4e00\u4e2aslice\u6216\u8005\u4f7f\u7528\u5b8c\u6574\u7684slice\u8868\u8fbe\u5f0f[low:high:max]\u9650\u5236\u6700\u5927\u5bb9\u91cf\uff0c\u6709\u52a9\u4e8e\u907f\u514d\u4ea7\u751f\u51b2\u7a81\u3002\u5f53\u60f3\u5bf9\u4e00\u4e2a\u5927slice\u8fdb\u884cshrink\u64cd\u4f5c\u65f6\uff0c\u4e24\u79cd\u65b9\u5f0f\u4e2d\uff0c\u53ea\u6709copy\u624d\u53ef\u4ee5\u907f\u514d\u5185\u5b58\u6cc4\u6f0f\u3002

"},{"location":"zh/#slice-26","title":"slice\u548c\u5185\u5b58\u6cc4\u6f0f (#26)","text":"

\u5bf9\u4e8eslice\u5143\u7d20\u4e3a\u6307\u9488\uff0c\u6216\u8005slice\u5143\u7d20\u4e3astruct\u4f46\u662f\u8be5struct\u542b\u6709\u6307\u9488\u5b57\u6bb5\uff0c\u5f53\u901a\u8fc7slice[low:high]\u64cd\u4f5c\u53d6subslice\u65f6\uff0c\u5bf9\u4e8e\u90a3\u4e9b\u4e0d\u53ef\u8bbf\u95ee\u7684\u5143\u7d20\u53ef\u4ee5\u663e\u793a\u8bbe\u7f6e\u4e3anil\u6765\u907f\u514d\u5185\u5b58\u6cc4\u9732\u3002

"},{"location":"zh/#map-27","title":"\u4e0d\u9ad8\u6548\u7684map\u521d\u59cb\u5316 (#27)","text":"

\u89c1 #21.

"},{"location":"zh/#map-28","title":"map\u548c\u5185\u5b58\u6cc4\u6f0f (#28)","text":"

\u4e00\u4e2amap\u7684buckets\u5360\u7528\u7684\u5185\u5b58\u53ea\u4f1a\u589e\u957f\uff0c\u4e0d\u4f1a\u7f29\u51cf\u3002\u56e0\u6b64\uff0c\u5982\u679c\u5b83\u5bfc\u81f4\u4e86\u4e00\u4e9b\u5185\u5b58\u5360\u7528\u7684\u95ee\u9898\uff0c\u4f60\u9700\u8981\u5c1d\u8bd5\u4e0d\u540c\u7684\u9009\u9879\u6765\u89e3\u51b3\uff0c\u6bd4\u5982\u91cd\u65b0\u521b\u5efa\u4e00\u4e2amap\u4ee3\u66ff\u539f\u6765\u7684\uff08\u539f\u6765\u7684map\u4f1a\u88abGC\u6389\uff09\uff0c\u6216\u8005map[keyType]valueType\u4e2d\u7684valueType\u4f7f\u7528\u6307\u9488\u4ee3\u66ff\u957f\u5ea6\u56fa\u5b9a\u7684\u6570\u7ec4\u6216\u8005sliceHeader\u6765\u7f13\u89e3\u8fc7\u591a\u7684\u5185\u5b58\u5360\u7528\u3002

"},{"location":"zh/#29","title":"\u4e0d\u6b63\u786e\u7684\u503c\u6bd4\u8f83 (#29)","text":"

Go\u4e2d\u6bd4\u8f83\u4e24\u4e2a\u7c7b\u578b\u503c\u65f6\uff0c\u5982\u679c\u662f\u53ef\u6bd4\u8f83\u7c7b\u578b\uff0c\u90a3\u4e48\u53ef\u4ee5\u4f7f\u7528 == \u6216\u8005 != \u8fd0\u7b97\u7b26\u8fdb\u884c\u6bd4\u8f83\uff0c\u6bd4\u5982\uff1abooleans\u3001numerals\u3001strings\u3001pointers\u3001channels\uff0c\u4ee5\u53ca\u5b57\u6bb5\u5168\u90e8\u662f\u53ef\u6bd4\u8f83\u7c7b\u578b\u7684structs\u3002\u5176\u4ed6\u60c5\u51b5\u4e0b\uff0c\u4f60\u53ef\u4ee5\u4f7f\u7528 reflect.DeepEqual \u6765\u6bd4\u8f83\uff0c\u7528\u53cd\u5c04\u7684\u8bdd\u4f1a\u727a\u7272\u4e00\u70b9\u6027\u80fd\uff0c\u4e5f\u53ef\u4ee5\u4f7f\u7528\u81ea\u5b9a\u4e49\u7684\u5b9e\u73b0\u548c\u5176\u4ed6\u5e93\u6765\u5b8c\u6210\u3002

"},{"location":"zh/#_3","title":"\u63a7\u5236\u7ed3\u6784","text":""},{"location":"zh/#range-30","title":"\u5ffd\u7565\u4e86 range \u5faa\u73af\u53d8\u91cf\u662f\u4e00\u4e2a\u62f7\u8d1d (#30)","text":"

range \u5faa\u73af\u4e2d\u7684\u5faa\u73af\u53d8\u91cf\u662f\u904d\u5386\u5bb9\u5668\u4e2d\u5143\u7d20\u503c\u7684\u4e00\u4e2a\u62f7\u8d1d\u3002\u56e0\u6b64\uff0c\u5982\u679c\u5143\u7d20\u503c\u662f\u4e00\u4e2astruct\u5e76\u4e14\u60f3\u5728 range \u4e2d\u4fee\u6539\u5b83\uff0c\u53ef\u4ee5\u901a\u8fc7\u7d22\u5f15\u503c\u6765\u8bbf\u95ee\u5e76\u4fee\u6539\u5b83\uff0c\u6216\u8005\u4f7f\u7528\u7ecf\u5178\u7684for\u5faa\u73af+\u7d22\u5f15\u503c\u7684\u5199\u6cd5\uff08\u9664\u975e\u904d\u5386\u7684\u5143\u7d20\u662f\u4e00\u4e2a\u6307\u9488\uff09\u3002

"},{"location":"zh/#range-channels-arrays-31","title":"\u5ffd\u7565\u4e86 range \u5faa\u73af\u4e2d\u8fed\u4ee3\u76ee\u6807\u503c\u7684\u8ba1\u7b97\u65b9\u5f0f (channels \u548c arrays) (#31)","text":"

\u4f20\u9012\u7ed9 range \u64cd\u4f5c\u7684\u8fed\u4ee3\u76ee\u6807\u5bf9\u5e94\u7684\u8868\u8fbe\u5f0f\u7684\u503c\uff0c\u53ea\u4f1a\u5728\u5faa\u73af\u6267\u884c\u524d\u88ab\u8ba1\u7b97\u4e00\u6b21\uff0c\u7406\u89e3\u8fd9\u4e2a\u6709\u52a9\u4e8e\u907f\u514d\u72af\u4e00\u4e9b\u5e38\u89c1\u7684\u9519\u8bef\uff0c\u4f8b\u5982\u4e0d\u9ad8\u6548\u7684channel\u8d4b\u503c\u64cd\u4f5c\u3001slice\u8fed\u4ee3\u64cd\u4f5c\u3002

"},{"location":"zh/#range-range-loops-32","title":"\u5ffd\u7565\u4e86 range \u5faa\u73af\u4e2d\u6307\u9488\u5143\u7d20\u7684\u5f71\u54cd range loops (#32)","text":"

\u8fd9\u91cc\u5176\u5b9e\u5f3a\u8c03\u7684\u662f range \u8fed\u4ee3\u8fc7\u7a0b\u4e2d\uff0c\u8fed\u4ee3\u53d8\u91cf\u5b9e\u9645\u4e0a\u662f\u4e00\u4e2a\u62f7\u8d1d\uff0c\u5047\u8bbe\u7ed9\u53e6\u5916\u4e00\u4e2a\u5bb9\u5668\u5143\u7d20\uff08\u6307\u9488\u7c7b\u578b\uff09\u8d4b\u503c\uff0c\u4e14\u9700\u8981\u5bf9\u8fed\u4ee3\u53d8\u91cf\u53d6\u5730\u5740\u8f6c\u6362\u6210\u6307\u9488\u518d\u8d4b\u503c\u7684\u8bdd\uff0c\u8fd9\u91cc\u6f5c\u85cf\u7740\u4e00\u4e2a\u9519\u8bef\uff0c\u5c31\u662ffor\u5faa\u73af\u8fed\u4ee3\u53d8\u91cf\u662f per-variable-per-loop \u800c\u4e0d\u662f per-variable-per-iteration\u3002\u5982\u679c\u662f\u901a\u8fc7\u5c40\u90e8\u53d8\u91cf\uff08\u7528\u8fed\u4ee3\u53d8\u91cf\u6765\u521d\u59cb\u5316\uff09\u6216\u8005\u4f7f\u7528\u7d22\u5f15\u503c\u6765\u76f4\u63a5\u5f15\u7528\u8fed\u4ee3\u7684\u5143\u7d20\uff0c\u5c06\u6709\u52a9\u4e8e\u907f\u514d\u62f7\u8d1d\u6307\u9488(\u8fed\u4ee3\u53d8\u91cf\u7684\u5730\u5740)\u4e4b\u7c7b\u7684bug\u3002

"},{"location":"zh/#map-33","title":"map\u8fed\u4ee3\u8fc7\u7a0b\u4e2d\u7684\u9519\u8bef\u5047\u8bbe\uff08\u904d\u5386\u987a\u5e8f \u548c \u8fed\u4ee3\u8fc7\u7a0b\u4e2d\u63d2\u5165\uff09(#33)","text":"

\u4f7f\u7528map\u65f6\uff0c\u4e3a\u4e86\u80fd\u5f97\u5230\u786e\u5b9a\u4e00\u81f4\u7684\u7ed3\u679c\uff0c\u5e94\u8be5\u8bb0\u4f4fGo\u4e2d\u7684map\u6570\u636e\u7ed3\u6784\uff1a * \u4e0d\u4f1a\u6309\u7167key\u5bf9data\u8fdb\u884c\u6392\u5e8f\uff0c\u904d\u5386\u65f6\u4e0d\u662f\u6309key\u6709\u5e8f\u7684\uff1b * \u904d\u5386\u65f6\u7684\u987a\u5e8f\uff0c\u4e5f\u4e0d\u662f\u6309\u7167\u63d2\u5165\u65f6\u7684\u987a\u5e8f\uff1b * \u6ca1\u6709\u4e00\u4e2a\u786e\u5b9a\u6027\u7684\u904d\u5386\u987a\u5e8f\uff0c\u6bcf\u6b21\u904d\u5386\u987a\u5e8f\u662f\u4e0d\u540c\u7684\uff1b * \u4e0d\u80fd\u4fdd\u8bc1\u8fed\u4ee3\u8fc7\u7a0b\u4e2d\u65b0\u63d2\u5165\u7684\u5143\u7d20\uff0c\u5728\u5f53\u524d\u8fed\u4ee3\u4e2d\u80fd\u591f\u88ab\u904d\u5386\u5230\uff1b

"},{"location":"zh/#break-34","title":"\u5ffd\u7565\u4e86 break \u8bed\u53e5\u662f\u5982\u4f55\u5de5\u4f5c\u7684 (#34)","text":"

\u914d\u5408label\u4f7f\u7528 break \u548c continue\uff0c\u80fd\u591f\u8df3\u8fc7\u4e00\u4e2a\u7279\u5b9a\u7684\u8bed\u53e5\uff0c\u5728\u67d0\u4e9b\u5faa\u73af\u4e2d\u5b58\u5728 switch\u548cselect\u8bed\u53e5\u7684\u573a\u666f\u4e2d\u5c31\u6bd4\u8f83\u6709\u5e2e\u52a9\u3002

"},{"location":"zh/#defer-35","title":"\u5728\u5faa\u73af\u4e2d\u4f7f\u7528 defer (#35)","text":"

\u5728\u5faa\u73af\u4e2d\u4f7f\u7528defer\u4e0d\u80fd\u5728\u6bcf\u8f6e\u8fed\u4ee3\u7ed3\u675f\u65f6\u6267\u884cdefer\u8bed\u53e5\uff0c\u4f46\u662f\u5c06\u5faa\u73af\u903b\u8f91\u63d0\u53d6\u5230\u51fd\u6570\u5185\u90e8\u4f1a\u5728\u6bcf\u6b21\u8fed\u4ee3\u7ed3\u675f\u65f6\u6267\u884c defer \u8bed\u53e5\u3002

"},{"location":"zh/#_4","title":"\u5b57\u7b26\u4e32","text":""},{"location":"zh/#rune-36","title":"\u6ca1\u6709\u7406\u89e3rune (#36)","text":"

\u7406\u89e3rune\u7c7b\u578b\u5bf9\u5e94\u7684\u662f\u4e00\u4e2aunicode\u7801\u70b9\uff0c\u6bcf\u4e00\u4e2aunicode\u7801\u70b9\u5176\u5b9e\u662f\u4e00\u4e2a\u591a\u5b57\u8282\u7684\u5e8f\u5217\uff0c\u4e0d\u662f\u4e00\u4e2abyte\u3002\u8fd9\u5e94\u8be5\u662fGo\u5f00\u53d1\u8005\u7684\u6838\u5fc3\u77e5\u8bc6\u70b9\u4e4b\u4e00\uff0c\u7406\u89e3\u4e86\u8fd9\u4e2a\u6709\u52a9\u4e8e\u66f4\u51c6\u786e\u5730\u5904\u7406\u5b57\u7b26\u4e32\u3002

"},{"location":"zh/#37","title":"\u4e0d\u6b63\u786e\u7684\u5b57\u7b26\u4e32\u904d\u5386 (#37)","text":"

\u4f7f\u7528 range \u64cd\u4f5c\u7b26\u5bf9\u4e00\u4e2astring\u8fdb\u884c\u904d\u5386\u5b9e\u9645\u4e0a\u662f\u5bf9string\u5bf9\u5e94\u7684 []rune \u8fdb\u884c\u904d\u5386\uff0c\u8fed\u4ee3\u53d8\u91cf\u4e2d\u7684\u7d22\u5f15\u503c\uff0c\u8868\u793a\u7684\u5f53\u524drune\u5bf9\u5e94\u7684 []byte \u5728\u6574\u4e2a []byte(string) \u4e2d\u7684\u8d77\u59cb\u7d22\u5f15\u3002\u5982\u679c\u8981\u8bbf\u95eestring\u4e2d\u7684\u67d0\u4e00\u4e2arune\uff08\u6bd4\u5982\u7b2c\u4e09\u4e2a\uff09\uff0c\u9996\u5148\u8981\u5c06\u5b57\u7b26\u4e32\u8f6c\u6362\u4e3a []rune \u7136\u540e\u518d\u6309\u7d22\u5f15\u503c\u8bbf\u95ee\u3002

"},{"location":"zh/#trim-38","title":"\u8bef\u7528trim\u51fd\u6570 (#38)","text":"

strings.TrimRight/strings.TrimLeft \u79fb\u9664\u5728\u5b57\u7b26\u4e32\u5c3e\u90e8\u6216\u8005\u5f00\u5934\u51fa\u73b0\u7684\u4e00\u4e9brunes\uff0c\u51fd\u6570\u4f1a\u6307\u5b9a\u4e00\u4e2arune\u96c6\u5408\uff0c\u51fa\u73b0\u5728\u96c6\u5408\u4e2d\u7684rune\u5c06\u88ab\u4ece\u5b57\u7b26\u4e32\u79fb\u9664\u3002\u800c strings.TrimSuffix/strings.TrimPrefix \u662f\u79fb\u9664\u5b57\u7b26\u4e32\u7684\u4e00\u4e2a\u540e\u7f00/\u524d\u7f00\u3002

"},{"location":"zh/#39","title":"\u4e0d\u7ecf\u4f18\u5316\u7684\u5b57\u7b26\u4e32\u62fc\u63a5\u64cd\u4f5c (#39)","text":"

\u5bf9\u4e00\u4e2a\u5b57\u7b26\u4e32\u5217\u8868\u8fdb\u884c\u904d\u5386\u62fc\u63a5\u64cd\u4f5c\uff0c\u5e94\u8be5\u901a\u8fc7 strings.Builder \u6765\u5b8c\u6210\uff0c\u4ee5\u907f\u514d\u6bcf\u6b21\u8fed\u4ee3\u62fc\u63a5\u65f6\u90fd\u5206\u914d\u4e00\u4e2a\u65b0\u7684string\u5bf9\u8c61\u51fa\u6765\u3002

"},{"location":"zh/#40","title":"\u65e0\u7528\u7684\u5b57\u7b26\u4e32\u8f6c\u6362 (#40)","text":"

bytes \u5305\u63d0\u4f9b\u4e86\u4e00\u4e9b\u548c strings \u5305\u76f8\u4f3c\u7684\u64cd\u4f5c\uff0c\u53ef\u4ee5\u5e2e\u52a9\u907f\u514d []byte/string \u4e4b\u95f4\u7684\u8f6c\u6362\u3002

"},{"location":"zh/#41","title":"\u5b50\u5b57\u7b26\u4e32\u548c\u5185\u5b58\u6cc4\u6f0f (#41)","text":"

\u4f7f\u7528\u4e00\u4e2a\u5b50\u5b57\u7b26\u4e32\u7684\u62f7\u8d1d\uff0c\u6709\u52a9\u4e8e\u907f\u514d\u5185\u5b58\u6cc4\u6f0f\uff0c\u56e0\u4e3a\u5bf9\u4e00\u4e2a\u5b57\u7b26\u4e32\u7684s[low:high]\u64cd\u4f5c\u8fd4\u56de\u7684\u5b50\u5b57\u7b26\u4e32\uff0c\u5176\u4f7f\u7528\u4e86\u548c\u539f\u5b57\u7b26\u4e32s\u76f8\u540c\u7684\u5e95\u5c42\u6570\u7ec4\u3002

"},{"location":"zh/#_5","title":"\u51fd\u6570\u548c\u65b9\u6cd5","text":""},{"location":"zh/#42","title":"\u4e0d\u77e5\u9053\u4f7f\u7528\u54ea\u79cd\u63a5\u6536\u5668\u7c7b\u578b (#42)","text":"

\u5bf9\u4e8e\u63a5\u6536\u5668\u7c7b\u578b\u662f\u91c7\u7528value\u7c7b\u578b\u8fd8\u662fpointer\u7c7b\u578b\uff0c\u5e94\u8be5\u53d6\u51b3\u4e8e\u4e0b\u9762\u8fd9\u51e0\u79cd\u56e0\u7d20\uff0c\u6bd4\u5982\uff1a\u65b9\u6cd5\u5185\u662f\u5426\u4f1a\u5bf9\u5b83\u8fdb\u884c\u4fee\u6539\uff0c\u5b83\u662f\u5426\u5305\u542b\u4e86\u4e00\u4e2a\u4e0d\u80fd\u88ab\u62f7\u8d1d\u7684\u5b57\u6bb5\uff0c\u4ee5\u53ca\u5b83\u8868\u793a\u7684\u5bf9\u8c61\u6709\u591a\u5927\u3002\u5982\u679c\u6709\u7591\u95ee\uff0c\u63a5\u6536\u5668\u53ef\u4ee5\u8003\u8651\u4f7f\u7528pointer\u7c7b\u578b\u3002

"},{"location":"zh/#43","title":"\u4ece\u4e0d\u4f7f\u7528\u547d\u540d\u7684\u8fd4\u56de\u503c (#43)","text":"

\u4f7f\u7528\u547d\u540d\u7684\u8fd4\u56de\u503c\uff0c\u662f\u4e00\u79cd\u6709\u6548\u6539\u5584\u51fd\u6570\u3001\u65b9\u6cd5\u53ef\u8bfb\u6027\u7684\u65b9\u6cd5\uff0c\u7279\u522b\u662f\u8fd4\u56de\u503c\u5217\u8868\u4e2d\u6709\u591a\u4e2a\u7c7b\u578b\u76f8\u540c\u7684\u53c2\u6570\u3002\u53e6\u5916\uff0c\u56e0\u4e3a\u8fd4\u56de\u503c\u5217\u8868\u4e2d\u7684\u53c2\u6570\u662f\u7ecf\u8fc7\u96f6\u503c\u521d\u59cb\u5316\u8fc7\u7684\uff0c\u67d0\u4e9b\u573a\u666f\u4e0b\u4e5f\u4f1a\u7b80\u5316\u51fd\u6570\u3001\u65b9\u6cd5\u7684\u5b9e\u73b0\u3002\u4f46\u662f\u9700\u8981\u6ce8\u610f\u5b83\u7684\u4e00\u4e9b\u6f5c\u5728\u526f\u4f5c\u7528\u3002

"},{"location":"zh/#44","title":"\u4f7f\u7528\u547d\u540d\u7684\u8fd4\u56de\u503c\u65f6\u9884\u671f\u5916\u7684\u526f\u4f5c\u7528 (#44)","text":"

\u89c1 #43.

\u4f7f\u7528\u547d\u540d\u7684\u8fd4\u56de\u503c\uff0c\u56e0\u4e3a\u5b83\u5df2\u7ecf\u88ab\u521d\u59cb\u5316\u4e86\u96f6\u503c\uff0c\u9700\u8981\u6ce8\u610f\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\u5f02\u5e38\u8fd4\u56de\u65f6\u662f\u5426\u9700\u8981\u7ed9\u5b83\u8d4b\u4e88\u4e00\u4e2a\u4e0d\u540c\u7684\u503c\uff0c\u6bd4\u5982\u8fd4\u56de\u503c\u5217\u8868\u5b9a\u4e49\u4e86\u4e00\u4e2a\u6709\u540d\u53c2\u6570 err error\uff0c\u9700\u8981\u6ce8\u610f return err \u65f6\u662f\u5426\u6b63\u786e\u5730\u5bf9 err \u8fdb\u884c\u4e86\u8d4b\u503c\u3002

"},{"location":"zh/#nil-45","title":"\u8fd4\u56de\u4e00\u4e2anil\u63a5\u6536\u5668 (#45)","text":"

\u5f53\u8fd4\u56de\u4e00\u4e2ainterface\u53c2\u6570\u65f6\uff0c\u9700\u8981\u5c0f\u5fc3\uff0c\u4e0d\u8981\u8fd4\u56de\u4e00\u4e2anil\u6307\u9488\uff0c\u800c\u662f\u5e94\u8be5\u663e\u793a\u8fd4\u56de\u4e00\u4e2anil\u503c\u3002\u5426\u5219\uff0c\u53ef\u80fd\u4f1a\u53d1\u751f\u4e00\u4e9b\u9884\u671f\u5916\u7684\u95ee\u9898\uff0c\u56e0\u4e3a\u8c03\u7528\u65b9\u4f1a\u6536\u5230\u4e00\u4e2a\u975enil\u7684\u503c\u3002

"},{"location":"zh/#46","title":"\u4f7f\u7528\u6587\u4ef6\u540d\u4f5c\u4e3a\u51fd\u6570\u5165\u53c2 (#46)","text":"

\u8bbe\u8ba1\u51fd\u6570\u65f6\u4f7f\u7528 io.Reader \u7c7b\u578b\u4f5c\u4e3a\u5165\u53c2\uff0c\u800c\u4e0d\u662f\u6587\u4ef6\u540d\uff0c\u5c06\u6709\u52a9\u4e8e\u6539\u5584\u51fd\u6570\u7684\u53ef\u590d\u7528\u6027\u3001\u6613\u6d4b\u8bd5\u6027\u3002

"},{"location":"zh/#defer-value-47","title":"\u5ffd\u7565 defer \u8bed\u53e5\u4e2d\u53c2\u6570\u3001\u63a5\u6536\u5668\u503c\u7684\u8ba1\u7b97\u65b9\u5f0f (\u53c2\u6570\u503c\u8ba1\u7b97, \u6307\u9488, \u548c value\u7c7b\u578b\u63a5\u6536\u5668) (#47)","text":"

\u4e3a\u4e86\u907f\u514d defer \u8bed\u53e5\u6267\u884c\u65f6\u5c31\u7acb\u5373\u8ba1\u7b97\u5bf9defer\u8981\u6267\u884c\u7684\u51fd\u6570\u7684\u53c2\u6570\u8fdb\u884c\u8ba1\u7b97\uff0c\u53ef\u4ee5\u8003\u8651\u5c06\u8981\u6267\u884c\u7684\u51fd\u6570\u653e\u5230\u95ed\u5305\u91cc\u9762\uff0c\u7136\u540e\u901a\u8fc7\u6307\u9488\u4f20\u9012\u53c2\u6570\u7ed9\u95ed\u5305\u5185\u51fd\u6570\uff08\u6216\u8005\u901a\u8fc7\u95ed\u5305\u6355\u83b7\u5916\u90e8\u53d8\u91cf\uff09\uff0c\u6765\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\u3002

"},{"location":"zh/#_6","title":"\u9519\u8bef\u7ba1\u7406","text":""},{"location":"zh/#panicking-48","title":"Panicking (#48)","text":"

\u4f7f\u7528 panic \u662fGo\u4e2d\u4e00\u79cd\u5904\u7406\u9519\u8bef\u7684\u65b9\u5f0f\uff0c\u4f46\u662f\u53ea\u80fd\u5728\u9047\u5230\u4e0d\u53ef\u6062\u590d\u7684\u9519\u8bef\u65f6\u4f7f\u7528\uff0c\u4f8b\u5982\uff1a\u901a\u77e5\u5f00\u53d1\u4eba\u5458\u4e00\u4e2a\u5f3a\u4f9d\u8d56\u7684\u6a21\u5757\u52a0\u8f7d\u5931\u8d25\u4e86\u3002

"},{"location":"zh/#error-49","title":"\u672a\u8003\u8651\u4f55\u65f6\u624d\u5e94\u8be5\u5305\u88c5error (#49)","text":"

Wrapping\uff08\u5305\u88c5\uff09\u9519\u8bef\u5141\u8bb8\u60a8\u6807\u8bb0\u9519\u8bef\u3001\u63d0\u4f9b\u989d\u5916\u7684\u4e0a\u4e0b\u6587\u4fe1\u606f\u3002\u7136\u800c\uff0c\u5305\u88c5\u9519\u8bef\u4f1a\u521b\u5efa\u6f5c\u5728\u7684\u8026\u5408\uff0c\u56e0\u4e3a\u5b83\u4f7f\u5f97\u539f\u6765\u7684\u9519\u8bef\u5bf9\u8c03\u7528\u8005\u53ef\u89c1\u3002\u5982\u679c\u60a8\u60f3\u8981\u9632\u6b62\u8fd9\u79cd\u60c5\u51b5\uff0c\u8bf7\u4e0d\u8981\u4f7f\u7528\u5305\u88c5\u9519\u8bef\u7684\u65b9\u5f0f\u3002

"},{"location":"zh/#50","title":"\u4e0d\u6b63\u786e\u7684\u9519\u8bef\u7c7b\u578b\u6bd4\u8f83 (#50)","text":"

\u5982\u679c\u4f60\u4f7f\u7528 Go 1.13 \u5f15\u5165\u7684\u7279\u6027 fmt.Errorf + %w \u6765\u5305\u88c5\u4e00\u4e2a\u9519\u8bef\uff0c\u5f53\u8fdb\u884c\u9519\u8bef\u6bd4\u8f83\u65f6\uff0c\u5982\u679c\u60f3\u5224\u65ad\u8be5\u5305\u88c5\u540e\u7684\u9519\u8bef\u662f\u4e0d\u662f\u6307\u5b9a\u7684\u9519\u8bef\u7c7b\u578b\uff0c\u5c31\u9700\u8981\u4f7f\u7528 errors.As\uff0c\u5982\u679c\u60f3\u5224\u65ad\u662f\u4e0d\u662f\u6307\u5b9a\u7684error\u5bf9\u8c61\u5c31\u9700\u8981\u7528 errors.Is\u3002

"},{"location":"zh/#51","title":"\u4e0d\u6b63\u786e\u7684\u9519\u8bef\u5bf9\u8c61\u503c\u6bd4\u8f83 (#51)","text":"

\u89c1 #50.

\u4e3a\u4e86\u8868\u8fbe\u4e00\u4e2a\u9884\u671f\u5185\u7684\u9519\u8bef\uff0c\u8bf7\u4f7f\u7528\u9519\u8bef\u503c\u7684\u65b9\u5f0f\uff0c\u5e76\u901a\u8fc7 == \u6216\u8005 errors.Is \u6765\u6bd4\u8f83\u3002\u800c\u5bf9\u4e8e\u610f\u5916\u9519\u8bef\uff0c\u5219\u5e94\u4f7f\u7528\u7279\u5b9a\u7684\u9519\u8bef\u7c7b\u578b\uff08\u53ef\u4ee5\u901a\u8fc7 errors.As \u6765\u6bd4\u8f83\uff09\u3002

"},{"location":"zh/#52","title":"\u5904\u7406\u540c\u4e00\u4e2a\u9519\u8bef\u4e24\u6b21 (#52)","text":"

\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u9519\u8bef\u4ec5\u9700\u8981\u5904\u7406\u4e00\u6b21\u3002\u6253\u5370\u9519\u8bef\u65e5\u5fd7\u4e5f\u662f\u4e00\u79cd\u9519\u8bef\u5904\u7406\u3002\u56e0\u6b64\uff0c\u5f53\u51fd\u6570\u5185\u53d1\u751f\u9519\u8bef\u65f6\uff0c\u5e94\u8be5\u5728\u6253\u5370\u65e5\u5fd7\u548c\u8fd4\u56de\u9519\u8bef\u4e2d\u9009\u62e9\u5176\u4e2d\u4e00\u79cd\u3002\u5305\u88c5\u9519\u8bef\u4e5f\u53ef\u4ee5\u63d0\u4f9b\u95ee\u9898\u53d1\u751f\u7684\u989d\u5916\u4e0a\u4e0b\u6587\u4fe1\u606f\uff0c\u4e5f\u5305\u62ec\u4e86\u539f\u6765\u7684\u9519\u8bef\uff08\u53ef\u8003\u8651\u4ea4\u7ed9\u8c03\u7528\u65b9\u8d1f\u8d23\u6253\u65e5\u5fd7\uff09\u3002

"},{"location":"zh/#53","title":"\u4e0d\u5904\u7406\u9519\u8bef (#53)","text":"

\u4e0d\u7ba1\u662f\u5728\u51fd\u6570\u8c03\u7528\u65f6\uff0c\u8fd8\u662f\u5728\u4e00\u4e2a defer \u51fd\u6570\u6267\u884c\u65f6\uff0c\u5982\u679c\u60f3\u8981\u5ffd\u7565\u4e00\u4e2a\u9519\u8bef\uff0c\u5e94\u8be5\u663e\u793a\u5730\u901a\u8fc7 _ \u6765\u5ffd\u7565\uff08\u53ef\u6ce8\u660e\u5ffd\u7565\u7684\u539f\u56e0\uff09\u3002\u5426\u5219\uff0c\u5c06\u6765\u7684\u8bfb\u8005\u5c31\u4f1a\u611f\u89c9\u5230\u56f0\u60d1\uff0c\u5ffd\u7565\u8fd9\u4e2a\u9519\u8bef\u662f\u6709\u610f\u4e3a\u4e4b\u8fd8\u662f\u65e0\u610f\u4e2d\u6f0f\u6389\u4e86\u3002

"},{"location":"zh/#defer-54","title":"\u4e0d\u5904\u7406 defer \u4e2d\u7684\u9519\u8bef (#54)","text":"

\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u4f60\u4e0d\u5e94\u8be5\u5ffd\u7565 defer \u51fd\u6570\u6267\u884c\u65f6\u8fd4\u56de\u7684\u9519\u8bef\uff0c\u6216\u8005\u663e\u793a\u5904\u7406\u5b83\uff0c\u6216\u8005\u5c06\u5b83\u4f20\u9012\u7ed9\u8c03\u7528\u65b9\u5904\u7406\uff0c\u53ef\u4ee5\u6839\u636e\u60c5\u666f\u8fdb\u884c\u9009\u62e9\u3002\u5982\u679c\u4f60\u786e\u5b9a\u8981\u5ffd\u7565\u8fd9\u4e2a\u9519\u8bef\uff0c\u8bf7\u663e\u793a\u4f7f\u7528 _ \u6765\u5ffd\u7565\u3002

"},{"location":"zh/#_7","title":"\u5e76\u53d1\u7f16\u7a0b: \u57fa\u7840","text":""},{"location":"zh/#55","title":"\u6df7\u6dc6\u5e76\u53d1\u548c\u5e76\u884c (#55)","text":"

\u7406\u89e3\u5e76\u53d1\uff08concurrency\uff09\u3001\u5e76\u884c\uff08parallelism\uff09\u4e4b\u95f4\u7684\u672c\u8d28\u533a\u522b\u662fGo\u5f00\u53d1\u4eba\u5458\u5fc5\u987b\u8981\u638c\u63e1\u7684\u3002\u5e76\u53d1\u662f\u5173\u4e8e\u7ed3\u6784\u8bbe\u8ba1\u4e0a\u7684\uff0c\u5e76\u884c\u662f\u5173\u4e8e\u5177\u4f53\u6267\u884c\u4e0a\u7684\u3002

"},{"location":"zh/#56","title":"\u8ba4\u4e3a\u5e76\u53d1\u603b\u662f\u66f4\u5feb (#56)","text":"

\u8981\u6210\u4e3a\u4e00\u540d\u719f\u7ec3\u7684\u5f00\u53d1\u4eba\u5458\uff0c\u60a8\u5fc5\u987b\u610f\u8bc6\u5230\u5e76\u975e\u6240\u6709\u573a\u666f\u4e0b\u90fd\u662f\u5e76\u53d1\u7684\u65b9\u6848\u66f4\u5feb\u3002\u5bf9\u4e8e\u4efb\u52a1\u4e2d\u7684\u6700\u5c0f\u5de5\u4f5c\u8d1f\u8f7d\u90e8\u5206\uff0c\u5bf9\u5b83\u4eec\u8fdb\u884c\u5e76\u884c\u5316\u5904\u7406\u5e76\u4e0d\u4e00\u5b9a\u5c31\u6709\u660e\u663e\u6536\u76ca\u6216\u8005\u6bd4\u4e32\u884c\u5316\u65b9\u6848\u66f4\u5feb\u3002\u5bf9\u4e32\u884c\u5316\u3001\u5e76\u53d1\u65b9\u6848\u8fdb\u884cbenchmark\u6d4b\u8bd5\uff0c\u662f\u9a8c\u8bc1\u5047\u8bbe\u7684\u597d\u529e\u6cd5\u3002

"},{"location":"zh/#channelsmutexes-57","title":"\u4e0d\u6e05\u695a\u4f55\u65f6\u4f7f\u7528channels\u6216mutexes (#57)","text":"

\u4e86\u89e3 goroutine \u4e4b\u95f4\u7684\u4ea4\u4e92\u4e5f\u53ef\u4ee5\u5728\u9009\u62e9\u4f7f\u7528channels\u6216mutexes\u65f6\u6709\u6240\u5e2e\u52a9\u3002\u4e00\u822c\u6765\u8bf4\uff0c\u5e76\u884c\u7684 goroutine \u9700\u8981\u540c\u6b65\uff0c\u56e0\u6b64\u9700\u8981\u4f7f\u7528mutexes\u3002\u76f8\u53cd\uff0c\u5e76\u53d1\u7684 goroutine \u901a\u5e38\u9700\u8981\u534f\u8c03\u548c\u7f16\u6392\uff0c\u56e0\u6b64\u9700\u8981\u4f7f\u7528channels\u3002

"},{"location":"zh/#vs-go-58","title":"\u4e0d\u660e\u767d\u7ade\u6001\u95ee\u9898 (\u6570\u636e\u7ade\u6001 vs. \u7ade\u6001\u6761\u4ef6 \u548c Go\u5185\u5b58\u6a21\u578b) (#58)","text":"

\u638c\u63e1\u5e76\u53d1\u610f\u5473\u7740\u8981\u8ba4\u8bc6\u5230\u6570\u636e\u7ade\u4e89\uff08data races\uff09\u548c\u7ade\u6001\u6761\u4ef6\uff08race conditions\uff09\u662f\u4e24\u4e2a\u4e0d\u540c\u7684\u6982\u5ff5\u3002\u6570\u636e\u7ade\u4e89\uff0c\u6307\u7684\u662f\u6709\u591a\u4e2agoroutines\u540c\u65f6\u8bbf\u95ee\u76f8\u540c\u5185\u5b58\u533a\u57df\u65f6\uff0c\u6ca1\u6709\u5fc5\u8981\u7684\u540c\u6b65\u63a7\u5236\uff0c\u4e14\u5176\u4e2d\u81f3\u5c11\u6709\u4e00\u4e2agoroutine\u662f\u6267\u884c\u7684\u5199\u64cd\u4f5c\u3002\u540c\u65f6\u8981\u8ba4\u8bc6\u5230\uff0c\u6ca1\u6709\u53d1\u751f\u6570\u636e\u7ade\u4e89\u4e0d\u4ee3\u8868\u7a0b\u5e8f\u7684\u6267\u884c\u662f\u786e\u5b9a\u6027\u7684\u3001\u6ca1\u95ee\u9898\u7684\u3002\u5f53\u5728\u67d0\u4e2a\u7279\u5b9a\u7684\u64cd\u4f5c\u987a\u5e8f\u6216\u8005\u7279\u5b9a\u7684\u4e8b\u4ef6\u53d1\u751f\u987a\u5e8f\u4e0b\uff0c\u5982\u679c\u6700\u7ec8\u7684\u884c\u4e3a\u662f\u4e0d\u53ef\u63a7\u7684\uff0c\u8fd9\u5c31\u662f\u7ade\u6001\u6761\u4ef6\u3002

ps\uff1a\u6570\u636e\u7ade\u4e89\u662f\u7ade\u6001\u6761\u4ef6\u7684\u5b50\u96c6\uff0c\u7ade\u6001\u6761\u4ef6\u4e0d\u4ec5\u5c40\u9650\u4e8e\u8bbf\u5b58\u672a\u540c\u6b65\uff0c\u5b83\u53ef\u4ee5\u53d1\u751f\u5728\u66f4\u9ad8\u7684\u5c42\u9762\u3002go test -race \u68c0\u6d4b\u7684\u662f\u6570\u636e\u7ade\u4e89\uff0c\u9700\u8981\u540c\u6b65\u6765\u89e3\u51b3\uff0c\u800c\u5f00\u53d1\u8005\u8fd8\u9700\u8981\u5173\u6ce8\u9762\u66f4\u5e7f\u7684\u7ade\u6001\u6761\u4ef6\uff0c\u5b83\u9700\u8981\u5bf9\u591a\u4e2agoroutines\u7684\u6267\u884c\u8fdb\u884c\u7f16\u6392\u3002

\u7406\u89e3 Go \u7684\u5185\u5b58\u6a21\u578b\u4ee5\u53ca\u6709\u5173\u987a\u5e8f\u548c\u540c\u6b65\u7684\u5e95\u5c42\u4fdd\u8bc1\u662f\u9632\u6b62\u53ef\u80fd\u7684\u6570\u636e\u7ade\u4e89\u548c\u7ade\u6001\u6761\u4ef6\u7684\u5173\u952e\u3002

"},{"location":"zh/#59","title":"\u4e0d\u7406\u89e3\u4e0d\u540c\u5de5\u4f5c\u8d1f\u8f7d\u7c7b\u578b\u5bf9\u5e76\u53d1\u7684\u5f71\u54cd (#59)","text":"

\u5f53\u521b\u5efa\u4e00\u5b9a\u6570\u91cf\u7684goroutines\u662f\uff0c\u9700\u8981\u8003\u8651\u5de5\u4f5c\u8d1f\u8f7d\u7684\u7c7b\u578b\u3002\u5982\u679c\u5de5\u4f5c\u8d1f\u8f7d\u662fCPU\u5bc6\u96c6\u578b\u7684\uff0c\u90a3\u4e48goroutines\u6570\u91cf\u5e94\u8be5\u63a5\u8fd1\u4e8e GOMAXPROCS \u7684\u503c\uff08\u8be5\u503c\u53d6\u51b3\u4e8e\u4e3b\u673a\u5904\u7406\u5668\u6838\u5fc3\u6570\uff09\u3002\u5982\u679c\u5de5\u4f5c\u8d1f\u8f7d\u662fIO\u5bc6\u96c6\u578b\u7684\uff0cgoroutines\u6570\u91cf\u5c31\u9700\u8981\u8003\u8651\u591a\u79cd\u56e0\u7d20\uff0c\u6bd4\u5982\u5916\u90e8\u7cfb\u7edf\uff08\u8003\u8651\u8bf7\u6c42\u3001\u54cd\u5e94\u901f\u7387\uff09\u3002

"},{"location":"zh/#go-contexts-60","title":"\u8bef\u89e3\u4e86Go contexts (#60)","text":"

Go \u7684\u4e0a\u4e0b\u6587\uff08context\uff09\u4e5f\u662f Go \u5e76\u53d1\u7f16\u7a0b\u7684\u57fa\u77f3\u4e4b\u4e00\u3002\u4e0a\u4e0b\u6587\u5141\u8bb8\u60a8\u643a\u5e26\u622a\u6b62\u65f6\u95f4\u3001\u53d6\u6d88\u4fe1\u53f7\u548c\u952e\u503c\u5217\u8868\u3002

"},{"location":"zh/#_8","title":"\u5e76\u53d1\u7f16\u7a0b: \u5b9e\u8df5","text":""},{"location":"zh/#context-61","title":"\u4f20\u9012\u4e0d\u5408\u9002\u7684context (#61)","text":"

\u5f53\u6211\u4eec\u4f20\u9012\u4e86\u4e00\u4e2acontext\uff0c\u6211\u4eec\u9700\u8981\u77e5\u9053\u8fd9\u4e2acontext\u4ec0\u4e48\u65f6\u5019\u53ef\u4ee5\u88ab\u53d6\u6d88\uff0c\u8fd9\u70b9\u5f88\u91cd\u8981\uff0c\u4f8b\u5982\uff1a\u4e00\u4e2aHTTP\u8bf7\u6c42\u5904\u7406\u5668\u5728\u53d1\u9001\u5b8c\u54cd\u5e94\u540e\u53d6\u6d88context\u3002

ps: \u5b9e\u9645\u4e0acontext\u8868\u8fbe\u7684\u662f\u4e00\u4e2a\u52a8\u4f5c\u53ef\u4ee5\u6301\u7eed\u591a\u4e45\u4e4b\u540e\u88ab\u505c\u6b62\u3002

"},{"location":"zh/#goroutine-62","title":"\u542f\u52a8\u4e86\u4e00\u4e2agoroutine\u4f46\u662f\u4e0d\u77e5\u9053\u5b83\u4f55\u65f6\u4f1a\u505c\u6b62 (#62)","text":"

\u907f\u514dgoroutine\u6cc4\u6f0f\uff0c\u8981\u6709\u8fd9\u79cd\u610f\u8bc6\uff0c\u5f53\u521b\u5efa\u5e76\u542f\u52a8\u4e00\u4e2agoroutine\u7684\u65f6\u5019\uff0c\u5e94\u8be5\u6709\u5bf9\u5e94\u7684\u8bbe\u8ba1\u8ba9\u5b83\u80fd\u6b63\u5e38\u9000\u51fa\u3002

"},{"location":"zh/#goroutines-63","title":"\u4e0d\u6ce8\u610f\u5904\u7406 goroutines \u548c \u5faa\u73af\u4e2d\u7684\u8fed\u4ee3\u53d8\u91cf (#63)","text":"

\u4e3a\u4e86\u907f\u514dgoroutines\u548c\u5faa\u73af\u4e2d\u7684\u8fed\u4ee3\u53d8\u91cf\u95ee\u9898\uff0c\u53ef\u4ee5\u8003\u8651\u521b\u5efa\u5c40\u90e8\u53d8\u91cf\u5e76\u5c06\u8fed\u4ee3\u53d8\u91cf\u8d4b\u503c\u7ed9\u5c40\u90e8\u53d8\u91cf\uff0c\u6216\u8005goroutine\u8c03\u7528\u5e26\u53c2\u6570\u7684\u51fd\u6570\uff0c\u5c06\u8fed\u4ee3\u53d8\u91cf\u503c\u4f5c\u4e3a\u53c2\u6570\u503c\u4f20\u5165\uff0c\u6765\u4ee3\u66ffgoroutine\u8c03\u7528\u95ed\u5305\u3002

"},{"location":"zh/#select-channels-64","title":"\u4f7f\u7528select + channels \u65f6\u8bef\u4ee5\u4e3a\u5206\u652f\u9009\u62e9\u987a\u5e8f\u662f\u786e\u5b9a\u7684 (#64)","text":"

\u8981\u660e\u767d\uff0cselect \u591a\u4e2achannels\u65f6\uff0c\u5982\u679c\u591a\u4e2achannels\u4e0a\u7684\u64cd\u4f5c\u90fd\u5c31\u7eea\uff0c\u90a3\u4e48\u4f1a\u968f\u673a\u9009\u62e9\u4e00\u4e2a case \u5206\u652f\u6765\u6267\u884c\uff0c\u56e0\u6b64\u8981\u907f\u514d\u6709\u5206\u652f\u9009\u62e9\u987a\u5e8f\u662f\u4ece\u4e0a\u5230\u4e0b\u7684\u8fd9\u79cd\u9519\u8bef\u9884\u8bbe\uff0c\u8fd9\u53ef\u80fd\u4f1a\u5bfc\u81f4\u8bbe\u8ba1\u4e0a\u7684bug\u3002

"},{"location":"zh/#channels-65","title":"\u4e0d\u6b63\u786e\u4f7f\u7528\u901a\u77e5 channels (#65)","text":"

\u53d1\u9001\u901a\u77e5\u65f6\u4f7f\u7528 chan struct{} \u7c7b\u578b\u3002

ps: \u5148\u660e\u767d\u4ec0\u4e48\u662f\u901a\u77e5channels\uff0c\u4e00\u4e2a\u901a\u77e5channels\u6307\u7684\u662f\u53ea\u662f\u7528\u6765\u505a\u901a\u77e5\uff0c\u800c\u5176\u4e2d\u4f20\u9012\u7684\u6570\u636e\u6ca1\u6709\u610f\u4e49\uff0c\u6216\u8005\u7406\u89e3\u6210\u4e0d\u4f20\u9012\u6570\u636e\u7684channels\uff0c\u8fd9\u79cd\u79f0\u4e3a\u901a\u77e5channels\u3002\u5176\u4e2d\u4f20\u9012\u7684\u6570\u636e\u7684\u7c7b\u578bstruct{}\u66f4\u5408\u9002\u3002

"},{"location":"zh/#nil-channels-66","title":"\u4e0d\u4f7f\u7528 nil channels (#66)","text":"

\u4f7f\u7528 nil channels \u5e94\u8be5\u662f\u5e76\u53d1\u5904\u7406\u65b9\u5f0f\u4e2d\u7684\u4e00\u90e8\u5206\uff0c\u4f8b\u5982\uff0c\u5b83\u80fd\u591f\u5e2e\u52a9\u7981\u7528 select \u8bed\u53e5\u4e2d\u7684\u7279\u5b9a\u7684\u5206\u652f\u3002

"},{"location":"zh/#channel-size-67","title":"\u4e0d\u6e05\u695a\u8be5\u5982\u4f55\u786e\u5b9a channel size (#67)","text":"

\u6839\u636e\u6307\u5b9a\u7684\u573a\u666f\u4ed4\u7ec6\u8bc4\u4f30\u5e94\u8be5\u4f7f\u7528\u54ea\u4e00\u79cd channel \u7c7b\u578b\uff08\u5e26\u7f13\u51b2\u7684\uff0c\u4e0d\u5e26\u7f13\u51b2\u7684\uff09\u3002\u53ea\u6709\u4e0d\u5e26\u7f13\u51b2\u7684 channels \u53ef\u4ee5\u63d0\u4f9b\u5f3a\u540c\u6b65\u4fdd\u8bc1\u3002

\u4f7f\u7528\u5e26\u7f13\u51b2\u7684 channels \u65f6\u5982\u679c\u4e0d\u786e\u5b9a size \u8be5\u5982\u4f55\u8bbe\u7f6e\uff0c\u53ef\u4ee5\u5148\u8bbe\u4e3a1\uff0c\u5982\u679c\u6709\u5408\u7406\u7684\u7406\u7531\u518d\u53bb\u6307\u5b9a channels size\u3002

ps: \u6839\u636edisruptor\u8fd9\u4e2a\u9ad8\u6027\u80fd\u5185\u5b58\u6d88\u606f\u961f\u5217\u7684\u5b9e\u8df5\uff0c\u5728\u67d0\u79cd\u8bfb\u5199pacing\u4e0b\uff0c\u961f\u5217\u8981\u4e48\u6ee1\u8981\u4e48\u7a7a\uff0c\u4e0d\u5927\u53ef\u80fd\u5904\u4e8e\u67d0\u79cd\u4ecb\u4e8e\u4e2d\u95f4\u7684\u7a33\u6001\u3002

"},{"location":"zh/#etcd-68","title":"\u5fd8\u8bb0\u4e86\u5b57\u7b26\u4e32\u683c\u5f0f\u5316\u53ef\u80fd\u5e26\u6765\u7684\u526f\u4f5c\u7528\uff08\u4f8b\u5982 etcd \u6570\u636e\u7ade\u4e89\u548c\u6b7b\u9501\uff09(#68)","text":"

\u610f\u8bc6\u5230\u5b57\u7b26\u4e32\u683c\u5f0f\u5316\u53ef\u80fd\u4f1a\u5bfc\u81f4\u8c03\u7528\u73b0\u6709\u51fd\u6570\uff0c\u8fd9\u610f\u5473\u7740\u9700\u8981\u6ce8\u610f\u53ef\u80fd\u7684\u6b7b\u9501\u548c\u5176\u4ed6\u6570\u636e\u7ade\u4e89\u95ee\u9898\u3002

ps: \u6838\u5fc3\u662f\u8981\u5173\u6ce8 fmt.Sprintf + %v \u8fdb\u884c\u5b57\u7b26\u4e32\u683c\u5f0f\u5316\u65f6 %v \u5177\u4f53\u5230\u4e0d\u540c\u7684\u7c7b\u578b\u503c\u65f6\uff0c\u5b9e\u9645\u4e0a\u6267\u884c\u7684\u64cd\u4f5c\u662f\u4ec0\u4e48\u3002\u6bd4\u5982 %v \u8fd9\u4e2aplaceholder\u5bf9\u5e94\u7684\u503c\u65f6\u4e00\u4e2a context.Context\uff0c\u90a3\u4e48\u4f1a\u5c31\u904d\u5386\u5176\u901a\u8fc7 context.WithValue \u9644\u52a0\u5728\u5176\u4e2d\u7684 values\uff0c\u8fd9\u4e2a\u8fc7\u7a0b\u53ef\u80fd\u6d89\u53ca\u5230\u6570\u636e\u7ade\u4e89\u95ee\u9898\u3002\u4e66\u4e2d\u63d0\u53ca\u7684\u53e6\u4e00\u4e2a\u5bfc\u81f4\u6b7b\u9501\u7684\u6848\u4f8b\u672c\u8d28\u4e0a\u4e5f\u662f\u4e00\u6837\u7684\u95ee\u9898\uff0c\u53ea\u4e0d\u8fc7\u53c8\u989d\u5916\u7275\u626f\u5230\u4e86 sync.RWMutex \u4e0d\u53ef\u91cd\u5165\u7684\u95ee\u9898\u3002

"},{"location":"zh/#append-69","title":"\u4f7f\u7528 append \u4e0d\u5f53\u5bfc\u81f4\u6570\u636e\u7ade\u4e89 (#69)","text":"

\u8c03\u7528 append \u4e0d\u603b\u662f\u6ca1\u6709\u6570\u636e\u7ade\u4e89\u7684\uff0c\u56e0\u6b64\u4e0d\u8981\u5728\u4e00\u4e2a\u5171\u4eab\u7684 slice \u4e0a\u5e76\u53d1\u5730\u6267\u884c append\u3002

"},{"location":"zh/#mutexes-slicesmaps-70","title":"\u8bef\u7528 mutexes \u548c slices\u3001maps (#70)","text":"

\u8bf7\u8bb0\u4f4f slices \u548c maps \u5f15\u7528\u7c7b\u578b\uff0c\u6709\u52a9\u4e8e\u907f\u514d\u5e38\u89c1\u7684\u6570\u636e\u7ade\u4e89\u95ee\u9898\u3002

ps: \u8fd9\u91cc\u5b9e\u9645\u662f\u56e0\u4e3a\u9519\u8bef\u7406\u89e3\u4e86 slices \u548c maps\uff0c\u5bfc\u81f4\u5199\u51fa\u4e86\u9519\u8bef\u7684\u62f7\u8d1d slices \u548c maps \u7684\u4ee3\u7801\uff0c\u8fdb\u800c\u5bfc\u81f4\u9501\u4fdd\u62a4\u65e0\u6548\u3001\u51fa\u73b0\u6570\u636e\u7ade\u4e89\u95ee\u9898\u3002

"},{"location":"zh/#syncwaitgroup-71","title":"\u8bef\u7528 sync.WaitGroup (#71)","text":"

\u6b63\u786e\u5730\u4f7f\u7528 sync.WaitGroup \u9700\u8981\u5728\u542f\u52a8 goroutines \u4e4b\u524d\u5148\u8c03\u7528 Add \u65b9\u6cd5\u3002

"},{"location":"zh/#synccond-72","title":"\u5fd8\u8bb0\u4f7f\u7528 sync.Cond (#72)","text":"

\u4f60\u53ef\u4ee5\u4f7f\u7528 sync.Cond \u5411\u591a\u4e2a goroutines \u53d1\u9001\u91cd\u590d\u7684\u901a\u77e5\u3002

"},{"location":"zh/#errgroup-73","title":"\u4e0d\u4f7f\u7528 errgroup (#73)","text":"

\u4f60\u53ef\u4ee5\u4f7f\u7528 errgroup \u5305\u6765\u540c\u6b65\u4e00\u7ec4 goroutines \u5e76\u5904\u7406\u9519\u8bef\u548c\u4e0a\u4e0b\u6587\u3002

"},{"location":"zh/#sync-74","title":"\u62f7\u8d1d\u4e00\u4e2a sync \u4e0b\u7684\u7c7b\u578b (#74)","text":"

sync \u5305\u4e0b\u7684\u7c7b\u578b\u4e0d\u5e94\u8be5\u88ab\u62f7\u8d1d\u3002

"},{"location":"zh/#_9","title":"\u6807\u51c6\u5e93","text":""},{"location":"zh/#timeduration-75","title":"\u4f7f\u7528\u4e86\u9519\u8bef\u7684 time.Duration (#75)","text":"

\u6ce8\u610f\u6709\u4e9b\u51fd\u6570\u63a5\u6536\u4e00\u4e2a time.Duration \u7c7b\u578b\u7684\u53c2\u6570\u65f6\uff0c\u5c3d\u7ba1\u76f4\u63a5\u4f20\u9012\u4e00\u4e2a\u6574\u6570\u662f\u53ef\u4ee5\u7684\uff0c\u4f46\u6700\u597d\u8fd8\u662f\u4f7f\u7528 time API \u4e2d\u7684\u65b9\u6cd5\u6765\u4f20\u9012 duration\uff0c\u4ee5\u907f\u514d\u53ef\u80fd\u9020\u6210\u7684\u56f0\u60d1\u3001bug\u3002

ps: \u91cd\u70b9\u662f\u6ce8\u610f time.Duration \u5b9a\u4e49\u7684\u662f nanoseconds \u6570\u3002

"},{"location":"zh/#timeafter-76","title":"time.After \u548c\u5185\u5b58\u6cc4\u6f0f (#76)","text":"

\u907f\u514d\u5728\u91cd\u590d\u6267\u884c\u5f88\u591a\u6b21\u7684\u51fd\u6570 \uff08\u5982\u5faa\u73af\u4e2d\u6216HTTP\u5904\u7406\u51fd\u6570\uff09\u4e2d\u8c03\u7528 time.After\uff0c\u8fd9\u53ef\u4ee5\u907f\u514d\u5185\u5b58\u5cf0\u503c\u6d88\u8017\u3002\u7531 time.After \u521b\u5efa\u7684\u8d44\u6e90\u4ec5\u5728\u8ba1\u65f6\u5668\u8d85\u65f6\u624d\u4f1a\u88ab\u91ca\u653e\u3002

"},{"location":"zh/#json-77","title":"JSON\u5904\u7406\u4e2d\u7684\u5e38\u89c1\u9519\u8bef (#77)","text":"
  • \u7c7b\u578b\u5d4c\u5957\u5bfc\u81f4\u7684\u9884\u6599\u5916\u7684\u884c\u4e3a

\u8981\u5f53\u5fc3\u5728Go\u7ed3\u6784\u4f53\u4e2d\u5d4c\u5165\u5b57\u6bb5\uff0c\u8fd9\u6837\u505a\u53ef\u80fd\u4f1a\u5bfc\u81f4\u8bf8\u5982\u5d4c\u5165\u7684 time.Time \u5b57\u6bb5\u5b9e\u73b0 json.Marshaler \u63a5\u53e3\uff0c\u4ece\u800c\u8986\u76d6\u9ed8\u8ba4\u7684json\u5e8f\u5217\u3002

  • JSON \u548c \u5355\u8c03\u65f6\u949f

\u5f53\u5bf9\u4e24\u4e2a time.Time \u7c7b\u578b\u503c\u8fdb\u884c\u6bd4\u8f83\u65f6\uff0c\u9700\u8981\u8bb0\u4f4f time.Time \u5305\u542b\u4e86\u4e00\u4e2a\u5899\u4e0a\u65f6\u949f\uff08wall clock\uff09\u548c\u4e00\u4e2a\u5355\u8c03\u65f6\u949f \uff08monotonic clock\uff09\uff0c\u800c\u4f7f\u7528 == \u8fd0\u7b97\u7b26\u8fdb\u884c\u6bd4\u8f83\u65f6\u4f1a\u540c\u65f6\u6bd4\u8f83\u8fd9\u4e24\u4e2a\u3002

  • Map \u952e\u5bf9\u5e94\u503c\u4e3a any

\u5f53\u63d0\u4f9b\u4e00\u4e2amap\u7528\u6765unmarshal JSON\u6570\u636e\u65f6\uff0c\u4e3a\u4e86\u907f\u514d\u4e0d\u786e\u5b9a\u7684value\u7ed3\u6784\u6211\u4eec\u4f1a\u4f7f\u7528 any \u6765\u4f5c\u4e3avalue\u7684\u7c7b\u578b\u800c\u4e0d\u662f\u5b9a\u4e49\u4e00\u4e2astruct\uff0c\u8fd9\u79cd\u60c5\u51b5\u4e0b\u9700\u8981\u8bb0\u5f97\u6570\u503c\u9ed8\u8ba4\u4f1a\u88ab\u8f6c\u6362\u4e3a float64\u3002

"},{"location":"zh/#sql-78","title":"\u5e38\u89c1\u7684 SQL \u9519\u8bef (#78)","text":"
  • \u5fd8\u8bb0\u4e86 sql.Open \u5e76\u6ca1\u6709\u4e0edb\u670d\u52a1\u5668\u5efa\u7acb\u5b9e\u9645\u8fde\u63a5

\u9700\u8981\u8c03\u7528 Ping \u6216\u8005 PingContext \u65b9\u6cd5\u6765\u6d4b\u8bd5\u914d\u7f6e\u5e76\u786e\u4fdd\u6570\u636e\u5e93\u662f\u53ef\u8fbe\u7684\u3002

  • \u5fd8\u8bb0\u4e86\u4f7f\u7528\u8fde\u63a5\u6c60

\u4f5c\u4e3a\u751f\u4ea7\u7ea7\u522b\u7684\u5e94\u7528\uff0c\u8bbf\u95ee\u6570\u636e\u5e93\u65f6\u5e94\u8be5\u5173\u6ce8\u914d\u7f6e\u6570\u636e\u5e93\u8fde\u63a5\u6c60\u53c2\u6570\u3002

  • \u6ca1\u6709\u4f7f\u7528 prepared \u8bed\u53e5

\u4f7f\u7528 SQL prepared \u8bed\u53e5\u80fd\u591f\u8ba9\u67e5\u8be2\u66f4\u52a0\u9ad8\u6548\u548c\u5b89\u5168\u3002

  • \u8bef\u5904\u7406 null \u503c

\u4f7f\u7528 sql.NullXXX \u7c7b\u578b\u5904\u7406\u8868\u4e2d\u7684\u53ef\u7a7a\u5217\u3002

  • \u4e0d\u5904\u7406\u884c\u8fed\u4ee3\u65f6\u7684\u9519\u8bef

\u8c03\u7528 sql.Rows \u7684 Err \u65b9\u6cd5\u6765\u786e\u4fdd\u5728\u51c6\u5907\u4e0b\u4e00\u4e2a\u884c\u65f6\u6ca1\u6709\u9057\u6f0f\u9519\u8bef\u3002

"},{"location":"zh/#http-sqlrows-osfile-79","title":"\u4e0d\u5173\u95ed\u4e34\u65f6\u8d44\u6e90\uff08HTTP \u8bf7\u6c42\u4f53\u3001sql.Rows \u548c os.File) (#79)","text":"

\u6700\u7ec8\u8981\u6ce8\u610f\u5173\u95ed\u6240\u6709\u5b9e\u73b0 io.Closer \u63a5\u53e3\u7684\u7ed3\u6784\u4f53,\u4ee5\u907f\u514d\u53ef\u80fd\u7684\u6cc4\u6f0f\u3002

"},{"location":"zh/#http-80","title":"\u54cd\u5e94HTTP\u8bf7\u6c42\u540e\u6ca1\u6709\u8fd4\u56de\u8bed\u53e5 (#80)","text":"

\u4e3a\u4e86\u907f\u514d\u5728HTTP\u5904\u7406\u51fd\u6570\u4e2d\u51fa\u73b0\u67d0\u4e9b\u610f\u5916\u7684\u95ee\u9898\uff0c\u5982\u679c\u60f3\u5728\u53d1\u751f http.Error \u540e\u8ba9HTTP\u5904\u7406\u51fd\u6570\u505c\u6b62\uff0c\u90a3\u4e48\u5c31\u4e0d\u8981\u5fd8\u8bb0\u4f7f\u7528return\u8bed\u53e5\u6765\u963b\u6b62\u540e\u7eed\u4ee3\u7801\u7684\u6267\u884c\u3002

"},{"location":"zh/#http-clientserver-81","title":"\u76f4\u63a5\u4f7f\u7528\u9ed8\u8ba4\u7684HTTP client\u548cserver (#81)","text":"

\u5bf9\u4e8e\u751f\u4ea7\u7ea7\u522b\u7684\u5e94\u7528\uff0c\u4e0d\u8981\u4f7f\u7528\u9ed8\u8ba4\u7684HTTP client\u548cserver\u5b9e\u73b0\u3002\u8fd9\u4e9b\u5b9e\u73b0\u7f3a\u5c11\u8d85\u65f6\u548c\u751f\u4ea7\u73af\u5883\u4e2d\u5e94\u8be5\u5f3a\u5236\u4f7f\u7528\u7684\u884c\u4e3a\u3002

"},{"location":"zh/#_10","title":"\u6d4b\u8bd5","text":""},{"location":"zh/#build-tags-82","title":"\u4e0d\u5bf9\u6d4b\u8bd5\u8fdb\u884c\u5206\u7c7b \uff08build tags, \u73af\u5883\u53d8\u91cf\uff0c\u77ed\u6a21\u5f0f\uff09(#82)","text":"

\u5bf9\u6d4b\u8bd5\u8fdb\u884c\u5fc5\u8981\u7684\u5206\u7c7b\uff0c\u53ef\u4ee5\u501f\u52a9 build tags\u3001\u73af\u5883\u53d8\u91cf\u4ee5\u53ca\u77ed\u6a21\u5f0f\uff0c\u6765\u4f7f\u5f97\u6d4b\u8bd5\u8fc7\u7a0b\u66f4\u52a0\u9ad8\u6548\u3002\u4f60\u53ef\u4ee5\u4f7f\u7528 build tags \u6216\u73af\u5883\u53d8\u91cf\u6765\u521b\u5efa\u6d4b\u8bd5\u7c7b\u522b\uff08\u4f8b\u5982\u5355\u5143\u6d4b\u8bd5\u4e0e\u96c6\u6210\u6d4b\u8bd5\uff09\uff0c\u5e76\u533a\u5206\u77ed\u6d4b\u8bd5\u4e0e\u957f\u65f6\u95f4\u6d4b\u8bd5\uff0c\u6765\u51b3\u5b9a\u6267\u884c\u54ea\u79cd\u7c7b\u578b\u7684\u3002

ps: \u4e86\u89e3\u4e0bgo build tags\uff0c\u4ee5\u53ca go test -short\u3002

"},{"location":"zh/#race-83","title":"\u4e0d\u6253\u5f00 race \u5f00\u5173 (#83)","text":"

\u6253\u5f00 -race \u5f00\u5173\u5728\u7f16\u5199\u5e76\u53d1\u5e94\u7528\u65f6\u975e\u5e38\u91cd\u8981\u3002\u8fd9\u80fd\u5e2e\u52a9\u4f60\u6355\u83b7\u53ef\u80fd\u7684\u6570\u636e\u7ade\u4e89,\u4ece\u800c\u907f\u514d\u8f6f\u4ef6bug\u3002

"},{"location":"zh/#parallel-shuffle-84","title":"\u4e0d\u6253\u5f00\u6d4b\u8bd5\u7684\u6267\u884c\u6a21\u5f0f\u5f00\u5173 (parallel \u548c shuffle) (#84)","text":"

\u6253\u5f00\u5f00\u5173 -parallel \u6709\u52a9\u4e8e\u52a0\u901f\u6d4b\u8bd5\u7684\u6267\u884c\uff0c\u7279\u522b\u662f\u6d4b\u8bd5\u4e2d\u5305\u542b\u4e00\u4e9b\u9700\u8981\u957f\u671f\u8fd0\u884c\u7684\u7528\u4f8b\u7684\u65f6\u5019\u3002

\u6253\u5f00\u5f00\u5173 -shuffle \u80fd\u591f\u6253\u4e71\u6d4b\u8bd5\u7528\u4f8b\u6267\u884c\u7684\u987a\u5e8f\uff0c\u907f\u514d\u4e00\u4e2a\u6d4b\u8bd5\u4f9d\u8d56\u4e8e\u67d0\u4e9b\u4e0d\u7b26\u5408\u771f\u5b9e\u60c5\u51b5\u7684\u9884\u8bbe\uff0c\u6709\u52a9\u4e8e\u53ca\u65e9\u66b4\u6f0fbug\u3002

"},{"location":"zh/#85","title":"\u4e0d\u4f7f\u7528\u8868\u9a71\u52a8\u7684\u6d4b\u8bd5 (#85)","text":"

\u8868\u9a71\u52a8\u7684\u6d4b\u8bd5\u662f\u4e00\u79cd\u6709\u6548\u7684\u65b9\u5f0f,\u53ef\u4ee5\u5c06\u4e00\u7ec4\u76f8\u4f3c\u7684\u6d4b\u8bd5\u5206\u7ec4\u5728\u4e00\u8d77,\u4ee5\u907f\u514d\u4ee3\u7801\u91cd\u590d\u548c\u4f7f\u672a\u6765\u7684\u66f4\u65b0\u66f4\u5bb9\u6613\u5904\u7406\u3002

"},{"location":"zh/#sleep-86","title":"\u5728\u5355\u5143\u6d4b\u8bd5\u4e2d\u6267\u884csleep\u64cd\u4f5c (#86)","text":"

\u4f7f\u7528\u540c\u6b65\u7684\u65b9\u5f0f\u3001\u907f\u514dsleep\uff0c\u6765\u5c3d\u91cf\u51cf\u5c11\u6d4b\u8bd5\u7684\u4e0d\u7a33\u5b9a\u6027\u548c\u63d0\u9ad8\u9c81\u68d2\u6027\u3002\u5982\u679c\u65e0\u6cd5\u4f7f\u7528\u540c\u6b65\u624b\u6bb5,\u53ef\u4ee5\u8003\u8651\u91cd\u8bd5\u7684\u65b9\u5f0f\u3002

"},{"location":"zh/#time-api-87","title":"\u6ca1\u6709\u9ad8\u6548\u5730\u5904\u7406 time API (#87)","text":"

\u7406\u89e3\u5982\u4f55\u5904\u7406\u4f7f\u7528 time API \u7684\u51fd\u6570\uff0c\u662f\u4f7f\u6d4b\u8bd5\u66f4\u52a0\u7a33\u5b9a\u7684\u53e6\u4e00\u79cd\u65b9\u5f0f\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u6807\u51c6\u6280\u672f\uff0c\u4f8b\u5982\u5c06\u65f6\u95f4\u4f5c\u4e3a\u9690\u85cf\u4f9d\u8d56\u9879\u7684\u4e00\u90e8\u5206\u6765\u5904\u7406\uff0c\u6216\u8005\u8981\u6c42\u5ba2\u6237\u7aef\u63d0\u4f9b\u65f6\u95f4\u3002

"},{"location":"zh/#httptest-iotest-88","title":"\u4e0d\u4f7f\u7528\u6d4b\u8bd5\u76f8\u5173\u7684\u5de5\u5177\u5305 (httptest \u548c iotest) (#88)","text":"

\u8fd9\u4e2a httptest \u5305\u5bf9\u5904\u7406HTTP\u5e94\u7528\u7a0b\u5e8f\u5f88\u6709\u5e2e\u52a9\u3002\u5b83\u63d0\u4f9b\u4e86\u4e00\u7ec4\u5b9e\u7528\u7a0b\u5e8f\u6765\u6d4b\u8bd5\u5ba2\u6237\u7aef\u548c\u670d\u52a1\u5668\u3002

\u8fd9\u4e2a iotest \u5305\u6709\u52a9\u4e8e\u7f16\u5199 io.Reader \u5e76\u6d4b\u8bd5\u5e94\u7528\u7a0b\u5e8f\u662f\u5426\u80fd\u591f\u5bb9\u5fcd\u9519\u8bef\u3002

"},{"location":"zh/#89","title":"\u4e0d\u6b63\u786e\u7684\u57fa\u51c6\u6d4b\u8bd5 (#89)","text":"
  • \u4e0d\u8981\u91cd\u7f6e\u6216\u8005\u6682\u505ctimer

\u4f7f\u7528 time \u65b9\u6cd5\u6765\u4fdd\u6301\u57fa\u51c6\u6d4b\u8bd5\u7684\u51c6\u786e\u6027\u3002

  • \u505a\u51fa\u9519\u8bef\u7684\u5fae\u57fa\u51c6\u6d4b\u8bd5\u5047\u8bbe

\u589e\u52a0 benchtime \u6216\u8005\u4f7f\u7528 benchstat \u7b49\u5de5\u5177\u53ef\u4ee5\u6709\u52a9\u4e8e\u5fae\u57fa\u51c6\u6d4b\u8bd5\u3002

\u5c0f\u5fc3\u5fae\u57fa\u51c6\u6d4b\u8bd5\u7684\u7ed3\u679c,\u5982\u679c\u6700\u7ec8\u8fd0\u884c\u5e94\u7528\u7a0b\u5e8f\u7684\u7cfb\u7edf\u4e0e\u8fd0\u884c\u5fae\u57fa\u51c6\u6d4b\u8bd5\u7684\u7cfb\u7edf\u4e0d\u540c\u3002

  • \u5bf9\u7f16\u8bd1\u671f\u4f18\u5316\u8981\u8db3\u591f\u5c0f\u5fc3

\u786e\u4fdd\u6d4b\u8bd5\u51fd\u6570\u662f\u5426\u4f1a\u4ea7\u751f\u4e00\u4e9b\u526f\u4f5c\u7528\uff0c\u9632\u6b62\u7f16\u8bd1\u5668\u4f18\u5316\u6b3a\u9a97\u4f60\u5f97\u5230\u7684\u57fa\u51c6\u6d4b\u8bd5\u7ed3\u679c\u3002

  • \u88ab\u89c2\u5bdf\u8005\u6548\u5e94\u6240\u6b3a\u9a97

\u4e3a\u4e86\u907f\u514d\u88ab\u89c2\u5bdf\u8005\u6548\u5e94\u6b3a\u9a97,\u5f3a\u5236\u91cd\u65b0\u521b\u5efaCPU\u5bc6\u96c6\u578b\u51fd\u6570\u4f7f\u7528\u7684\u6570\u636e\u3002

"},{"location":"zh/#go-test-90","title":"\u6ca1\u6709\u53bb\u63a2\u7d22go test\u6240\u6709\u7684\u7279\u6027 (#90)","text":"
  • \u4ee3\u7801\u8986\u76d6\u7387

\u4f7f\u7528 -coverprofile \u53c2\u6570\u53ef\u4ee5\u5feb\u901f\u67e5\u770b\u4ee3\u7801\u7684\u6d4b\u8bd5\u8986\u76d6\u60c5\u51b5\uff0c\u65b9\u4fbf\u5feb\u901f\u67e5\u770b\u54ea\u4e2a\u90e8\u5206\u9700\u8981\u66f4\u591a\u7684\u5173\u6ce8\u3002

  • \u5728\u4e00\u4e2a\u4e0d\u540c\u5305\u4e2d\u6267\u884c\u6d4b\u8bd5

\u5355\u5143\u6d4b\u8bd5\u7ec4\u7ec7\u5230\u4e00\u4e2a\u72ec\u7acb\u7684\u5305\u4e2d\uff0c\u5bf9\u4e8e\u5bf9\u5916\u5c42\u66b4\u6f0f\u7684\u63a5\u53e3\uff0c\u9700\u8981\u5199\u4e00\u4e9b\u6d4b\u8bd5\u7528\u4f8b\u3002\u6d4b\u8bd5\u5e94\u8be5\u5173\u6ce8\u516c\u5f00\u7684\u884c\u4e3a\uff0c\u800c\u975e\u5185\u90e8\u5b9e\u73b0\u7ec6\u8282\u3002

  • Utility \u51fd\u6570

\u5904\u7406\u9519\u8bef\u65f6,\u4f7f\u7528 *testing.T \u53d8\u91cf\u800c\u4e0d\u662f\u7ecf\u5178\u7684 if err != nil \u53ef\u4ee5\u8ba9\u4ee3\u7801\u66f4\u52a0\u7b80\u6d01\u6613\u8bfb\u3002

  • \u8bbe\u7f6e\u548c\u9500\u6bc1

\u4f60\u53ef\u4ee5\u4f7f\u7528setup\u548cteardown\u51fd\u6570\u6765\u914d\u7f6e\u4e00\u4e2a\u590d\u6742\u7684\u73af\u5883\uff0c\u6bd4\u5982\u5728\u96c6\u6210\u6d4b\u8bd5\u7684\u60c5\u51b5\u4e0b\u3002

"},{"location":"zh/#_11","title":"\u4e0d\u4f7f\u7528\u6a21\u7cca\u6d4b\u8bd5 (\u793e\u533a\u53cd\u9988\u9519\u8bef)","text":"

\u6a21\u7cca\u6d4b\u8bd5\u662f\u4e00\u79cd\u9ad8\u6548\u7684\u7b56\u7565\uff0c\u4f7f\u7528\u5b83\u80fd\u68c0\u6d4b\u51fa\u968f\u673a\u3001\u610f\u6599\u5916\u7684\u548c\u4e00\u4e9b\u6076\u610f\u7684\u6570\u636e\u8f93\u5165\uff0c\u6765\u5b8c\u6210\u4e00\u4e9b\u590d\u6742\u7684\u64cd\u4f5c\u3002

\u89c1: @jeromedoucet

"},{"location":"zh/#_12","title":"\u4f18\u5316\u6280\u672f","text":""},{"location":"zh/#cpu-cache-91","title":"\u4e0d\u7406\u89e3CPU cache (#91)","text":"
  • CPU\u67b6\u6784

\u7406\u89e3CPU\u7f13\u5b58\u7684\u4f7f\u7528\u5bf9\u4e8e\u4f18\u5316CPU\u5bc6\u96c6\u578b\u5e94\u7528\u5f88\u91cd\u8981\uff0c\u56e0\u4e3aL1\u7f13\u5b58\u6bd4\u4e3b\u5b58\u5feb50\u5230100\u500d\u3002

  • Cache line

\u610f\u8bc6\u5230 cache line \u6982\u5ff5\u5bf9\u4e8e\u7406\u89e3\u5982\u4f55\u5728\u6570\u636e\u5bc6\u96c6\u578b\u5e94\u7528\u4e2d\u7ec4\u7ec7\u6570\u636e\u975e\u5e38\u5173\u952e\u3002CPU \u5e76\u4e0d\u662f\u4e00\u4e2a\u4e00\u4e2a\u5b57\u6765\u83b7\u53d6\u5185\u5b58\u3002\u76f8\u53cd\uff0c\u5b83\u901a\u5e38\u590d\u5236\u4e00\u4e2a 64\u5b57\u8282\u957f\u5ea6\u7684 cache line\u3002\u4e3a\u4e86\u83b7\u5f97\u6bcf\u4e2a cache line \u7684\u6700\u5927\u6548\u7528\uff0c\u9700\u8981\u5b9e\u65bd\u7a7a\u95f4\u5c40\u90e8\u6027\u3002

  • \u4e00\u7cfb\u5217struct\u5143\u7d20\u6784\u6210\u7684slice vs. \u591a\u4e2aslice\u5b57\u6bb5\u6784\u6210\u7684struct

  • \u6982\u7387\u6027\u7684\u95ee\u9898

\u63d0\u9ad8CPU\u6267\u884c\u4ee3\u7801\u65f6\u7684\u53ef\u9884\u6d4b\u6027\uff0c\u4e5f\u662f\u4f18\u5316\u67d0\u4e9b\u51fd\u6570\u7684\u4e00\u4e2a\u6709\u6548\u65b9\u6cd5\u3002\u6bd4\u5982\uff0c\u56fa\u5b9a\u6b65\u957f\u6216\u8fde\u7eed\u8bbf\u95ee\u5bf9CPU\u6765\u8bf4\u662f\u53ef\u9884\u6d4b\u7684\uff0c\u4f46\u975e\u8fde\u7eed\u8bbf\u95ee\uff08\u4f8b\u5982\u94fe\u8868\uff09\u5c31\u662f\u4e0d\u53ef\u9884\u6d4b\u7684\u3002

  • cache\u653e\u7f6e\u7b56\u7565

\u8981\u6ce8\u610f\u73b0\u4ee3\u7f13\u5b58\u662f\u5206\u533a\u7684\uff08set associative placement\uff0c\u7ec4\u76f8\u8fde\u6620\u5c04\uff09\uff0c\u8981\u6ce8\u610f\u907f\u514d\u4f7f\u7528 critical stride\uff0c\u8fd9\u79cd\u6b65\u957f\u60c5\u51b5\u4e0b\u53ea\u80fd\u5229\u7528 cache \u7684\u4e00\u5c0f\u90e8\u5206\u3002

critical stride\uff0c\u8fd9\u79cd\u7c7b\u578b\u7684\u6b65\u957f\uff0c\u6307\u7684\u662f\u5185\u5b58\u8bbf\u95ee\u7684\u6b65\u957f\u521a\u597d\u7b49\u4e8e cache \u5927\u5c0f\u3002\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u53ea\u6709\u5c11\u90e8\u5206 cacheline \u88ab\u5229\u7528\u3002

"},{"location":"zh/#false-sharing-92","title":"\u5199\u7684\u5e76\u53d1\u5904\u7406\u903b\u8f91\u4f1a\u5bfc\u81f4false sharing (#92)","text":"

\u4e86\u89e3 CPU \u7f13\u5b58\u7684\u8f83\u4f4e\u5c42\u7684 L1\u3001L2 cache \u4e0d\u4f1a\u5728\u6240\u6709\u6838\u95f4\u5171\u4eab\uff0c\u7f16\u5199\u5e76\u53d1\u5904\u7406\u903b\u8f91\u65f6\u80fd\u907f\u514d\u5199\u51fa\u4e00\u4e9b\u964d\u4f4e\u6027\u80fd\u7684\u95ee\u9898\uff0c\u6bd4\u5982\u4f2a\u5171\u4eab\uff08false sharing\uff09\u3002\u5185\u5b58\u5171\u4eab\u53ea\u662f\u4e00\u79cd\u5047\u8c61\u3002

"},{"location":"zh/#93","title":"\u6ca1\u6709\u8003\u8651\u6307\u4ee4\u7ea7\u7684\u5e76\u884c (#93)","text":"

\u4f7f\u7528\u6307\u4ee4\u7ea7\u5e76\u884c\uff08ILP\uff09\u4f18\u5316\u4ee3\u7801\u7684\u7279\u5b9a\u90e8\u5206\uff0c\u4ee5\u5141\u8bb8CPU\u5c3d\u53ef\u80fd\u6267\u884c\u66f4\u591a\u53ef\u4ee5\u5e76\u884c\u6267\u884c\u7684\u6307\u4ee4\u3002\u8bc6\u522b\u6307\u4ee4\u7684\u6570\u636e\u4f9d\u8d56\u95ee\u9898\uff08data hazards\uff09\u662f\u4e3b\u8981\u6b65\u9aa4\u4e4b\u4e00\u3002

"},{"location":"zh/#94","title":"\u4e0d\u4e86\u89e3\u6570\u636e\u5bf9\u9f50 (#94)","text":"

\u8bb0\u4f4fGo\u4e2d\u57fa\u672c\u7c7b\u578b\u4e0e\u5176\u81ea\u8eab\u5927\u5c0f\u5bf9\u9f50\uff0c\u4f8b\u5982\uff0c\u6309\u964d\u5e8f\u4ece\u5927\u5230\u5c0f\u91cd\u65b0\u7ec4\u7ec7\u7ed3\u6784\u4f53\u7684\u5b57\u6bb5\u53ef\u4ee5\u5f62\u6210\u66f4\u7d27\u51d1\u7684\u7ed3\u6784\u4f53\uff08\u51cf\u5c11\u5185\u5b58\u5206\u914d\uff0c\u66f4\u597d\u7684\u7a7a\u95f4\u5c40\u90e8\u6027\uff09\uff0c\u8fd9\u6709\u52a9\u4e8e\u907f\u514d\u4e00\u4e9b\u5e38\u89c1\u7684\u9519\u8bef\u3002

"},{"location":"zh/#stack-vs-heap-95","title":"\u4e0d\u4e86\u89e3 stack vs. heap (#95)","text":"

\u4e86\u89e3\u5806\u548c\u6808\u4e4b\u95f4\u7684\u533a\u522b\u662f\u5f00\u53d1\u4eba\u5458\u7684\u6838\u5fc3\u77e5\u8bc6\u70b9\uff0c\u7279\u522b\u662f\u8981\u53bb\u4f18\u5316\u4e00\u4e2aGo\u7a0b\u5e8f\u65f6\u3002\u6808\u5206\u914d\u7684\u5f00\u9500\u51e0\u4e4e\u4e3a\u96f6\uff0c\u800c\u5806\u5206\u914d\u5219\u8f83\u6162\uff0c\u5e76\u4e14\u4f9d\u8d56GC\u6765\u6e05\u7406\u5185\u5b58\u3002

"},{"location":"zh/#api-compiler-optimizations-and-syncpool-96","title":"\u4e0d\u77e5\u9053\u5982\u4f55\u51cf\u5c11\u5185\u5b58\u5206\u914d\u6b21\u6570 (API\u8c03\u6574, compiler optimizations, and sync.Pool) (#96)","text":"

\u51cf\u5c11\u5185\u5b58\u5206\u914d\u6b21\u6570\u4e5f\u662f\u4f18\u5316Go\u5e94\u7528\u7684\u4e00\u4e2a\u91cd\u8981\u65b9\u9762\u3002\u8fd9\u53ef\u4ee5\u901a\u8fc7\u4e0d\u540c\u7684\u65b9\u5f0f\u6765\u5b9e\u73b0,\u6bd4\u5982\u4ed4\u7ec6\u8bbe\u8ba1API\u6765\u907f\u514d\u4e0d\u5fc5\u8981\u7684\u62f7\u8d1d\uff0c\u4ee5\u53ca\u4f7f\u7528 sync.Pool \u6765\u5bf9\u5f85\u5206\u914d\u5bf9\u8c61\u8fdb\u884c\u6c60\u5316\u3002

"},{"location":"zh/#97","title":"\u4e0d\u6ce8\u610f\u4f7f\u7528\u5185\u8054 (#97)","text":"

\u4f7f\u7528\u5feb\u901f\u8def\u5f84\u7684\u5185\u8054\u6280\u672f\u6765\u66f4\u52a0\u6709\u6548\u5730\u51cf\u5c11\u8c03\u7528\u51fd\u6570\u7684\u644a\u9500\u65f6\u95f4\u3002

"},{"location":"zh/#go-98","title":"\u4e0d\u4f7f\u7528Go\u95ee\u9898\u8bca\u65ad\u5de5\u5177 (#98)","text":"

\u4e86\u89e3Go profilng\u5de5\u5177\u3001\u6267\u884c\u65f6tracer\u6765\u8f85\u52a9\u5224\u65ad\u4e00\u4e2a\u5e94\u7528\u7a0b\u5e8f\u662f\u5426\u6b63\u5e38\uff0c\u4ee5\u53ca\u5217\u51fa\u9700\u8981\u4f18\u5316\u7684\u90e8\u5206\u3002

"},{"location":"zh/#gc-99","title":"\u4e0d\u7406\u89e3GC\u662f\u5982\u4f55\u5de5\u4f5c\u7684 (#99)","text":"

\u7406\u89e3\u5982\u4f55\u8c03\u4f18GC\u80fd\u591f\u5e26\u6765\u5f88\u591a\u6536\u76ca\uff0c\u4f8b\u5982\u6709\u52a9\u4e8e\u66f4\u9ad8\u6548\u5730\u5904\u7406\u7a81\u589e\u7684\u8d1f\u8f7d\u3002

"},{"location":"zh/#dockerk8sgo-100","title":"\u4e0d\u4e86\u89e3Docker\u6216\u8005K8S\u5bf9\u8fd0\u884c\u7684Go\u5e94\u7528\u7684\u6027\u80fd\u5f71\u54cd (#100)","text":"

\u4e3a\u4e86\u907f\u514dCPU throttling\uff08CPU\u9650\u9891\uff09\u95ee\u9898\uff0c\u5f53\u6211\u4eec\u5728Docker\u548cKubernetes\u90e8\u7f72\u5e94\u7528\u65f6\uff0c\u8981\u77e5\u9053Go\u8bed\u8a00\u5bf9CFS(\u5b8c\u5168\u516c\u5e73\u8c03\u5ea6\u5668)\u65e0\u611f\u77e5\u3002

"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 00000000..92418620 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,73 @@ + + + + https://100go.co/ + 2024-03-10 + daily + + + https://100go.co/20-slice/ + 2024-03-10 + daily + + + https://100go.co/28-maps-memory-leaks/ + 2024-03-10 + daily + + + https://100go.co/5-interface-pollution/ + 2024-03-10 + daily + + + https://100go.co/56-concurrency-faster/ + 2024-03-10 + daily + + + https://100go.co/89-benchmarks/ + 2024-03-10 + daily + + + https://100go.co/9-generics/ + 2024-03-10 + daily + + + https://100go.co/92-false-sharing/ + 2024-03-10 + daily + + + https://100go.co/98-profiling-execution-tracing/ + 2024-03-10 + daily + + + https://100go.co/book/ + 2024-03-10 + daily + + + https://100go.co/chapter-1/ + 2024-03-10 + daily + + + https://100go.co/external/ + 2024-03-10 + daily + + + https://100go.co/ja/ + 2024-03-10 + daily + + + https://100go.co/zh/ + 2024-03-10 + daily + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 00000000..1557d486 Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/stylesheets/extra.css b/stylesheets/extra.css new file mode 100644 index 00000000..4180724a --- /dev/null +++ b/stylesheets/extra.css @@ -0,0 +1,8 @@ +.md-typeset figure img { + display: inline; +} + +.md-typeset .admonition, +.md-typeset details { + font-size: 15px +} diff --git a/zh/index.html b/zh/index.html new file mode 100644 index 00000000..ec02d67d --- /dev/null +++ b/zh/index.html @@ -0,0 +1,3615 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Chinese (Simplified) Version - 100 Go Mistakes and How to Avoid Them + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

100个Go常见错误及如何避免

+
+Jobs +

Is your company hiring? Sponsor the Chinese version of this repository and let a significant audience of Go developers (~1k unique visitors per week) know about your opportunities in this section.

+
+

+

代码及工程组织

+

意外的变量隐藏 (#1)

+

避免变量隐藏(外部作用域变量被内部作用域同名变量隐藏),有助于避免变量引用错误,有助于他人阅读理解。

+

不必要的代码嵌套 (#2)

+

避免不必要的、过多的嵌套层次,并且让正常代码路径尽量左对齐(而不是放在分支路径中),有助于构建可读性更好的代码。

+

误用init函数 (#3)

+

初始化变量时,请记住 init 函数具有有限的错误处理能力,并且会使状态处理和测试变得更加复杂。在大多数情况下,初始化应该作为特定函数来处理。

+

滥用getters/setters (#4)

+

在Go语言中,强制使用getter和setter方法并不符合Go惯例。在实践中,应该找到效率和盲目遵循某些惯用法之间的平衡点。

+

接口污染 (#5)

+

抽象应该被发现,而不是被创造。为了避免不必要的复杂性,需要时才创建接口,而不是预见到需要它,或者至少可以证明这种抽象是有价值的。

+

将接口定义在实现方一侧 (#6)

+

将接口保留在引用方一侧(而不是实现方一侧)可以避免不必要的抽象。

+

将接口作为返回值 (#7)

+

为了避免在灵活性方面受到限制,大多数情况下函数不应该返回接口,而应该返回具体的实现。相反,函数应该尽可能地使用接口作为参数。

+

any 没传递任何信息 (#8)

+

只有在需要接受或返回任意类型时,才使用 any,例如 json.Marshal。其他情况下,因为 any 不提供有意义的信息,可能会导致编译时问题,如允许调用者调用方法处理任意类型数据。

+

困惑何时该用范型 (#9)

+

使用泛型,可以通过类型参数分离具体的数据类型和行为,避免写很多重复度很高的代码。然而,不要过早地使用泛型、类型参数,只有在你看到真正需要时才使用。否则,它们会引入不必要的抽象和复杂性。

+

未意识到类型嵌套的可能问题 (#10)

+

使用类型嵌套也可以避免写一些重复代码,然而,在使用时需要确保不会导致不合理的可见性问题,比如有些字段应该对外隐藏不应该被暴漏。

+

不使用function option模式 (#11)

+

为了设计并提供更友好的API(可选参数),为了更好地处理选项,应该使用function option模式。

+

工程组织不合理 (工程结构和包的组织) (#12)

+

遵循像 project-layout 的建议来组织Go工程是一个不错的方法,特别是你正在寻找一些类似的经验、惯例来组织一个新的Go工程的时候。

+

创建工具包 (#13)

+

命名是软件设计开发中非常重要的一个部分,创建一些名如 commonutilshared 之类的包名并不会给读者带来太大价值,应该将这些包名重构为更清晰、更聚焦的包名。

+

忽略了包名冲突 (#14)

+

为了避免变量名和包名之间的冲突,导致混淆或甚至错误,应为每个变量和包使用唯一的名称。如果这不可行,可以考虑使用导入别名 import importAlias 'importPath',以区分包名和变量名,或者考虑一个更好的变量名。

+

代码缺少文档 (#15)

+

为了让使用方、维护人员能更清晰地了解你的代码的意图,导出的元素(函数、类型、字段)需要添加godoc注释。

+

不使用linters检查 (#16)

+

为了改善代码质量、整体代码的一致性,应该使用linters、formatters。

+

数据类型

+

八进制字面量引发的困惑 (#17)

+

在阅读现有代码时,请记住以 0 开头的整数字面量是八进制数。此外,为了提高可读性,可以通过在前面加上 0o 来显式地表示八进制整数。

+

未注意可能的整数溢出 (#18)

+

在 Go 中整数上溢出和下溢是静默处理的,所以你可以实现自己的函数来捕获它们。

+

没有透彻理解浮点数 (#19)

+

比较浮点数时,通过比较二者的delta值是否介于一定的范围内,能让你写出可移植性更好的代码。

+

在进行加法或减法时,将具有相似数量级的操作分成同一组以提高精度 (过早指数对齐丢失精度)。此外,在进行加法和减法之前,应先进行乘法和除法 (加减法误差会被乘除放大)。

+

不理解slice的长度和容量 (#20)

+

理解slice的长度和容量的区别,是一个Go开发者的核心知识点之一。slice的长度指的是slice已经存储的元素的数量,而容量指的是slice当前底层开辟的数组最多能容纳的元素的数量。

+

不高效的slice初始化 (#21)

+

当创建一个slice时,如果其长度可以预先确定,那么可以在定义时指定它的长度和容量。这可以改善后期append时一次或者多次的内存分配操作,从而改善性能。对于map的初始化也是这样的。

+

困惑于nil和空slice (#22)

+

为了避免常见的对nil和empty slice处理行为的混淆,例如在使用 encoding/json 或 reflect 包时,你需要理解 nil 和 empty slice的区别。两者都是长度为零、容量为零的切片,但是 nil 切片不需要分配内存。

+

没有适当检查slice是否为空 (#23)

+

检查一个slice的是否包含任何元素,可以检查其长度,不管slice是nil还是empty,检查长度都是有效的。这个检查方法也适用于map。

+

为了设计更明确的API,API不应区分nil和空切片。

+

没有正确拷贝slice (#24)

+

使用 copy 拷贝一个slice元素到另一个slice时,需要记得,实际拷贝的元素数量是二者slice长度中的较小值。

+

slice append带来的预期之外的副作用 (#25)

+

如果两个不同的函数操作的slice复用了相同的底层数组,它们对slice执行append操作时可能会产生冲突。使用copy来完整复制一个slice或者使用完整的slice表达式[low:high:max]限制最大容量,有助于避免产生冲突。当想对一个大slice进行shrink操作时,两种方式中,只有copy才可以避免内存泄漏。

+

slice和内存泄漏 (#26)

+

对于slice元素为指针,或者slice元素为struct但是该struct含有指针字段,当通过slice[low:high]操作取subslice时,对于那些不可访问的元素可以显示设置为nil来避免内存泄露。

+

不高效的map初始化 (#27)

+

#21.

+

map和内存泄漏 (#28)

+

一个map的buckets占用的内存只会增长,不会缩减。因此,如果它导致了一些内存占用的问题,你需要尝试不同的选项来解决,比如重新创建一个map代替原来的(原来的map会被GC掉),或者map[keyType]valueType中的valueType使用指针代替长度固定的数组或者sliceHeader来缓解过多的内存占用。

+

不正确的值比较 (#29)

+

Go中比较两个类型值时,如果是可比较类型,那么可以使用 == 或者 != 运算符进行比较,比如:booleans、numerals、strings、pointers、channels,以及字段全部是可比较类型的structs。其他情况下,你可以使用 reflect.DeepEqual 来比较,用反射的话会牺牲一点性能,也可以使用自定义的实现和其他库来完成。

+

控制结构

+

忽略了 range 循环变量是一个拷贝 (#30)

+

range 循环中的循环变量是遍历容器中元素值的一个拷贝。因此,如果元素值是一个struct并且想在 range 中修改它,可以通过索引值来访问并修改它,或者使用经典的for循环+索引值的写法(除非遍历的元素是一个指针)。

+

忽略了 range 循环中迭代目标值的计算方式 (channels 和 arrays) (#31)

+

传递给 range 操作的迭代目标对应的表达式的值,只会在循环执行前被计算一次,理解这个有助于避免犯一些常见的错误,例如不高效的channel赋值操作、slice迭代操作。

+

忽略了 range 循环中指针元素的影响 range loops (#32)

+

这里其实强调的是 range 迭代过程中,迭代变量实际上是一个拷贝,假设给另外一个容器元素(指针类型)赋值,且需要对迭代变量取地址转换成指针再赋值的话,这里潜藏着一个错误,就是for循环迭代变量是 per-variable-per-loop 而不是 per-variable-per-iteration。如果是通过局部变量(用迭代变量来初始化)或者使用索引值来直接引用迭代的元素,将有助于避免拷贝指针(迭代变量的地址)之类的bug。

+

map迭代过程中的错误假设(遍历顺序 和 迭代过程中插入)(#33)

+

使用map时,为了能得到确定一致的结果,应该记住Go中的map数据结构: +* 不会按照key对data进行排序,遍历时不是按key有序的; +* 遍历时的顺序,也不是按照插入时的顺序; +* 没有一个确定性的遍历顺序,每次遍历顺序是不同的; +* 不能保证迭代过程中新插入的元素,在当前迭代中能够被遍历到;

+

忽略了 break 语句是如何工作的 (#34)

+

配合label使用 breakcontinue,能够跳过一个特定的语句,在某些循环中存在 switchselect语句的场景中就比较有帮助。

+

在循环中使用 defer (#35)

+

在循环中使用defer不能在每轮迭代结束时执行defer语句,但是将循环逻辑提取到函数内部会在每次迭代结束时执行 defer 语句。

+

字符串

+

没有理解rune (#36)

+

理解rune类型对应的是一个unicode码点,每一个unicode码点其实是一个多字节的序列,不是一个byte。这应该是Go开发者的核心知识点之一,理解了这个有助于更准确地处理字符串。

+

不正确的字符串遍历 (#37)

+

使用 range 操作符对一个string进行遍历实际上是对string对应的 []rune 进行遍历,迭代变量中的索引值,表示的当前rune对应的 []byte 在整个 []byte(string) 中的起始索引。如果要访问string中的某一个rune(比如第三个),首先要将字符串转换为 []rune 然后再按索引值访问。

+

误用trim函数 (#38)

+

strings.TrimRight/strings.TrimLeft 移除在字符串尾部或者开头出现的一些runes,函数会指定一个rune集合,出现在集合中的rune将被从字符串移除。而 strings.TrimSuffix/strings.TrimPrefix 是移除字符串的一个后缀/前缀。

+

不经优化的字符串拼接操作 (#39)

+

对一个字符串列表进行遍历拼接操作,应该通过 strings.Builder 来完成,以避免每次迭代拼接时都分配一个新的string对象出来。

+

无用的字符串转换 (#40)

+

bytes 包提供了一些和 strings 包相似的操作,可以帮助避免 []byte/string 之间的转换。

+

子字符串和内存泄漏 (#41)

+

使用一个子字符串的拷贝,有助于避免内存泄漏,因为对一个字符串的s[low:high]操作返回的子字符串,其使用了和原字符串s相同的底层数组。

+

函数和方法

+

不知道使用哪种接收器类型 (#42)

+

对于接收器类型是采用value类型还是pointer类型,应该取决于下面这几种因素,比如:方法内是否会对它进行修改,它是否包含了一个不能被拷贝的字段,以及它表示的对象有多大。如果有疑问,接收器可以考虑使用pointer类型。

+

从不使用命名的返回值 (#43)

+

使用命名的返回值,是一种有效改善函数、方法可读性的方法,特别是返回值列表中有多个类型相同的参数。另外,因为返回值列表中的参数是经过零值初始化过的,某些场景下也会简化函数、方法的实现。但是需要注意它的一些潜在副作用。

+

使用命名的返回值时预期外的副作用 (#44)

+

#43.

+

使用命名的返回值,因为它已经被初始化了零值,需要注意在某些情况下异常返回时是否需要给它赋予一个不同的值,比如返回值列表定义了一个有名参数 err error,需要注意 return err 时是否正确地对 err 进行了赋值。

+

返回一个nil接收器 (#45)

+

当返回一个interface参数时,需要小心,不要返回一个nil指针,而是应该显示返回一个nil值。否则,可能会发生一些预期外的问题,因为调用方会收到一个非nil的值。

+

使用文件名作为函数入参 (#46)

+

设计函数时使用 io.Reader 类型作为入参,而不是文件名,将有助于改善函数的可复用性、易测试性。

+

忽略 defer 语句中参数、接收器值的计算方式 (参数值计算, 指针, 和 value类型接收器) (#47)

+

为了避免 defer 语句执行时就立即计算对defer要执行的函数的参数进行计算,可以考虑将要执行的函数放到闭包里面,然后通过指针传递参数给闭包内函数(或者通过闭包捕获外部变量),来解决这个问题。

+

错误管理

+

Panicking (#48)

+

使用 panic 是Go中一种处理错误的方式,但是只能在遇到不可恢复的错误时使用,例如:通知开发人员一个强依赖的模块加载失败了。

+

未考虑何时才应该包装error (#49)

+

Wrapping(包装)错误允许您标记错误、提供额外的上下文信息。然而,包装错误会创建潜在的耦合,因为它使得原来的错误对调用者可见。如果您想要防止这种情况,请不要使用包装错误的方式。

+

不正确的错误类型比较 (#50)

+

如果你使用 Go 1.13 引入的特性 fmt.Errorf + %w 来包装一个错误,当进行错误比较时,如果想判断该包装后的错误是不是指定的错误类型,就需要使用 errors.As,如果想判断是不是指定的error对象就需要用 errors.Is

+

不正确的错误对象值比较 (#51)

+

#50.

+

为了表达一个预期内的错误,请使用错误值的方式,并通过 == 或者 errors.Is 来比较。而对于意外错误,则应使用特定的错误类型(可以通过 errors.As 来比较)。

+

处理同一个错误两次 (#52)

+

大多数情况下,错误仅需要处理一次。打印错误日志也是一种错误处理。因此,当函数内发生错误时,应该在打印日志和返回错误中选择其中一种。包装错误也可以提供问题发生的额外上下文信息,也包括了原来的错误(可考虑交给调用方负责打日志)。

+

不处理错误 (#53)

+

不管是在函数调用时,还是在一个 defer 函数执行时,如果想要忽略一个错误,应该显示地通过 _ 来忽略(可注明忽略的原因)。否则,将来的读者就会感觉到困惑,忽略这个错误是有意为之还是无意中漏掉了。

+

不处理 defer 中的错误 (#54)

+

大多数情况下,你不应该忽略 defer 函数执行时返回的错误,或者显示处理它,或者将它传递给调用方处理,可以根据情景进行选择。如果你确定要忽略这个错误,请显示使用 _ 来忽略。

+

并发编程: 基础

+

混淆并发和并行 (#55)

+

理解并发(concurrency)、并行(parallelism)之间的本质区别是Go开发人员必须要掌握的。并发是关于结构设计上的,并行是关于具体执行上的。

+

认为并发总是更快 (#56)

+

要成为一名熟练的开发人员,您必须意识到并非所有场景下都是并发的方案更快。对于任务中的最小工作负载部分,对它们进行并行化处理并不一定就有明显收益或者比串行化方案更快。对串行化、并发方案进行benchmark测试,是验证假设的好办法。

+

不清楚何时使用channels或mutexes (#57)

+

了解 goroutine 之间的交互也可以在选择使用channels或mutexes时有所帮助。一般来说,并行的 goroutine 需要同步,因此需要使用mutexes。相反,并发的 goroutine 通常需要协调和编排,因此需要使用channels。

+

不明白竞态问题 (数据竞态 vs. 竞态条件 和 Go内存模型) (#58)

+

掌握并发意味着要认识到数据竞争(data races)和竞态条件(race conditions)是两个不同的概念。数据竞争,指的是有多个goroutines同时访问相同内存区域时,没有必要的同步控制,且其中至少有一个goroutine是执行的写操作。同时要认识到,没有发生数据竞争不代表程序的执行是确定性的、没问题的。当在某个特定的操作顺序或者特定的事件发生顺序下,如果最终的行为是不可控的,这就是竞态条件。

+
+

ps:数据竞争是竞态条件的子集,竞态条件不仅局限于访存未同步,它可以发生在更高的层面。go test -race 检测的是数据竞争,需要同步来解决,而开发者还需要关注面更广的竞态条件,它需要对多个goroutines的执行进行编排。

+
+

理解 Go 的内存模型以及有关顺序和同步的底层保证是防止可能的数据竞争和竞态条件的关键。

+

不理解不同工作负载类型对并发的影响 (#59)

+

当创建一定数量的goroutines是,需要考虑工作负载的类型。如果工作负载是CPU密集型的,那么goroutines数量应该接近于 GOMAXPROCS 的值(该值取决于主机处理器核心数)。如果工作负载是IO密集型的,goroutines数量就需要考虑多种因素,比如外部系统(考虑请求、响应速率)。

+

误解了Go contexts (#60)

+

Go 的上下文(context)也是 Go 并发编程的基石之一。上下文允许您携带截止时间、取消信号和键值列表。

+

并发编程: 实践

+

传递不合适的context (#61)

+

当我们传递了一个context,我们需要知道这个context什么时候可以被取消,这点很重要,例如:一个HTTP请求处理器在发送完响应后取消context。

+
+

ps: 实际上context表达的是一个动作可以持续多久之后被停止。

+
+

启动了一个goroutine但是不知道它何时会停止 (#62)

+

避免goroutine泄漏,要有这种意识,当创建并启动一个goroutine的时候,应该有对应的设计让它能正常退出。

+

不注意处理 goroutines 和 循环中的迭代变量 (#63)

+

为了避免goroutines和循环中的迭代变量问题,可以考虑创建局部变量并将迭代变量赋值给局部变量,或者goroutine调用带参数的函数,将迭代变量值作为参数值传入,来代替goroutine调用闭包。

+

使用select + channels 时误以为分支选择顺序是确定的 (#64)

+

要明白,select 多个channels时,如果多个channels上的操作都就绪,那么会随机选择一个 case 分支来执行,因此要避免有分支选择顺序是从上到下的这种错误预设,这可能会导致设计上的bug。

+

不正确使用通知 channels (#65)

+

发送通知时使用 chan struct{} 类型。

+
+

ps: 先明白什么是通知channels,一个通知channels指的是只是用来做通知,而其中传递的数据没有意义,或者理解成不传递数据的channels,这种称为通知channels。其中传递的数据的类型struct{}更合适。

+
+

不使用 nil channels (#66)

+

使用 nil channels 应该是并发处理方式中的一部分,例如,它能够帮助禁用 select 语句中的特定的分支。

+

不清楚该如何确定 channel size (#67)

+

根据指定的场景仔细评估应该使用哪一种 channel 类型(带缓冲的,不带缓冲的)。只有不带缓冲的 channels 可以提供强同步保证。

+

使用带缓冲的 channels 时如果不确定 size 该如何设置,可以先设为1,如果有合理的理由再去指定 channels size。

+
+

ps: 根据disruptor这个高性能内存消息队列的实践,在某种读写pacing下,队列要么满要么空,不大可能处于某种介于中间的稳态。

+
+

忘记了字符串格式化可能带来的副作用(例如 etcd 数据竞争和死锁)(#68)

+

意识到字符串格式化可能会导致调用现有函数,这意味着需要注意可能的死锁和其他数据竞争问题。

+
+

ps: 核心是要关注 fmt.Sprintf + %v 进行字符串格式化时 %v 具体到不同的类型值时,实际上执行的操作是什么。比如 %v 这个placeholder对应的值时一个 context.Context,那么会就遍历其通过 context.WithValue 附加在其中的 values,这个过程可能涉及到数据竞争问题。书中提及的另一个导致死锁的案例本质上也是一样的问题,只不过又额外牵扯到了 sync.RWMutex 不可重入的问题。

+
+

使用 append 不当导致数据竞争 (#69)

+

调用 append 不总是没有数据竞争的,因此不要在一个共享的 slice 上并发地执行 append

+

误用 mutexes 和 slices、maps (#70)

+

请记住 slices 和 maps 引用类型,有助于避免常见的数据竞争问题。

+
+

ps: 这里实际是因为错误理解了 slices 和 maps,导致写出了错误的拷贝 slices 和 maps 的代码,进而导致锁保护无效、出现数据竞争问题。

+
+

误用 sync.WaitGroup (#71)

+

正确地使用 sync.WaitGroup 需要在启动 goroutines 之前先调用 Add 方法。

+

忘记使用 sync.Cond (#72)

+

你可以使用 sync.Cond 向多个 goroutines 发送重复的通知。

+

不使用 errgroup (#73)

+

你可以使用 errgroup 包来同步一组 goroutines 并处理错误和上下文。

+

拷贝一个 sync 下的类型 (#74)

+

sync 包下的类型不应该被拷贝。

+

标准库

+

使用了错误的 time.Duration (#75)

+

注意有些函数接收一个 time.Duration 类型的参数时,尽管直接传递一个整数是可以的,但最好还是使用 time API 中的方法来传递 duration,以避免可能造成的困惑、bug。

+
+

ps: 重点是注意 time.Duration 定义的是 nanoseconds 数。

+
+

time.After 和内存泄漏 (#76)

+

避免在重复执行很多次的函数 (如循环中或HTTP处理函数)中调用 time.After,这可以避免内存峰值消耗。由 time.After 创建的资源仅在计时器超时才会被释放。

+

JSON处理中的常见错误 (#77)

+
    +
  • 类型嵌套导致的预料外的行为
  • +
+

要当心在Go结构体中嵌入字段,这样做可能会导致诸如嵌入的 time.Time 字段实现 json.Marshaler 接口,从而覆盖默认的json序列。

+
    +
  • JSON 和 单调时钟
  • +
+

当对两个 time.Time 类型值进行比较时,需要记住 time.Time 包含了一个墙上时钟(wall clock)和一个单调时钟 (monotonic clock),而使用 == 运算符进行比较时会同时比较这两个。

+
    +
  • Map 键对应值为 any
  • +
+

当提供一个map用来unmarshal JSON数据时,为了避免不确定的value结构我们会使用 any 来作为value的类型而不是定义一个struct,这种情况下需要记得数值默认会被转换为 float64

+

常见的 SQL 错误 (#78)

+
    +
  • 忘记了 sql.Open 并没有与db服务器建立实际连接
  • +
+

需要调用 Ping 或者 PingContext 方法来测试配置并确保数据库是可达的。

+
    +
  • 忘记了使用连接池
  • +
+

作为生产级别的应用,访问数据库时应该关注配置数据库连接池参数。

+
    +
  • 没有使用 prepared 语句
  • +
+

使用 SQL prepared 语句能够让查询更加高效和安全。

+
    +
  • 误处理 null 值
  • +
+

使用 sql.NullXXX 类型处理表中的可空列。

+
    +
  • 不处理行迭代时的错误
  • +
+

调用 sql.RowsErr 方法来确保在准备下一个行时没有遗漏错误。

+

不关闭临时资源(HTTP 请求体、sql.Rowsos.File) (#79)

+

最终要注意关闭所有实现 io.Closer 接口的结构体,以避免可能的泄漏。

+

响应HTTP请求后没有返回语句 (#80)

+

为了避免在HTTP处理函数中出现某些意外的问题,如果想在发生 http.Error 后让HTTP处理函数停止,那么就不要忘记使用return语句来阻止后续代码的执行。

+

直接使用默认的HTTP client和server (#81)

+

对于生产级别的应用,不要使用默认的HTTP client和server实现。这些实现缺少超时和生产环境中应该强制使用的行为。

+

测试

+

不对测试进行分类 (build tags, 环境变量,短模式)(#82)

+

对测试进行必要的分类,可以借助 build tags、环境变量以及短模式,来使得测试过程更加高效。你可以使用 build tags 或环境变量来创建测试类别(例如单元测试与集成测试),并区分短测试与长时间测试,来决定执行哪种类型的。

+
+

ps: 了解下go build tags,以及 go test -short

+
+

不打开 race 开关 (#83)

+

打开 -race 开关在编写并发应用时非常重要。这能帮助你捕获可能的数据竞争,从而避免软件bug。

+

不打开测试的执行模式开关 (parallel 和 shuffle) (#84)

+

打开开关 -parallel 有助于加速测试的执行,特别是测试中包含一些需要长期运行的用例的时候。

+

打开开关 -shuffle 能够打乱测试用例执行的顺序,避免一个测试依赖于某些不符合真实情况的预设,有助于及早暴漏bug。

+

不使用表驱动的测试 (#85)

+

表驱动的测试是一种有效的方式,可以将一组相似的测试分组在一起,以避免代码重复和使未来的更新更容易处理。

+

在单元测试中执行sleep操作 (#86)

+

使用同步的方式、避免sleep,来尽量减少测试的不稳定性和提高鲁棒性。如果无法使用同步手段,可以考虑重试的方式。

+

没有高效地处理 time API (#87)

+

理解如何处理使用 time API 的函数,是使测试更加稳定的另一种方式。您可以使用标准技术,例如将时间作为隐藏依赖项的一部分来处理,或者要求客户端提供时间。

+

不使用测试相关的工具包 (httptestiotest) (#88)

+

这个 httptest 包对处理HTTP应用程序很有帮助。它提供了一组实用程序来测试客户端和服务器。

+

这个 iotest 包有助于编写 io.Reader 并测试应用程序是否能够容忍错误。

+

不正确的基准测试 (#89)

+
    +
  • 不要重置或者暂停timer
  • +
+

使用 time 方法来保持基准测试的准确性。

+
    +
  • 做出错误的微基准测试假设
  • +
+

增加 benchtime 或者使用 benchstat 等工具可以有助于微基准测试。

+

小心微基准测试的结果,如果最终运行应用程序的系统与运行微基准测试的系统不同。

+
    +
  • 对编译期优化要足够小心
  • +
+

确保测试函数是否会产生一些副作用,防止编译器优化欺骗你得到的基准测试结果。

+
    +
  • 被观察者效应所欺骗
  • +
+

为了避免被观察者效应欺骗,强制重新创建CPU密集型函数使用的数据。

+

没有去探索go test所有的特性 (#90)

+
    +
  • 代码覆盖率
  • +
+

使用 -coverprofile 参数可以快速查看代码的测试覆盖情况,方便快速查看哪个部分需要更多的关注。

+
    +
  • 在一个不同包中执行测试
  • +
+

单元测试组织到一个独立的包中,对于对外层暴漏的接口,需要写一些测试用例。测试应该关注公开的行为,而非内部实现细节。

+
    +
  • Utility 函数
  • +
+

处理错误时,使用 *testing.T 变量而不是经典的 if err != nil 可以让代码更加简洁易读。

+
    +
  • 设置和销毁
  • +
+

你可以使用setup和teardown函数来配置一个复杂的环境,比如在集成测试的情况下。

+

不使用模糊测试 (社区反馈错误)

+

模糊测试是一种高效的策略,使用它能检测出随机、意料外的和一些恶意的数据输入,来完成一些复杂的操作。

+

见: @jeromedoucet

+

优化技术

+

不理解CPU cache (#91)

+
    +
  • CPU架构
  • +
+

理解CPU缓存的使用对于优化CPU密集型应用很重要,因为L1缓存比主存快50到100倍。

+
    +
  • Cache line
  • +
+

意识到 cache line 概念对于理解如何在数据密集型应用中组织数据非常关键。CPU 并不是一个一个字来获取内存。相反,它通常复制一个 64字节长度的 cache line。为了获得每个 cache line 的最大效用,需要实施空间局部性。

+
    +
  • +

    一系列struct元素构成的slice vs. 多个slice字段构成的struct

    +
  • +
  • +

    概率性的问题

    +
  • +
+

提高CPU执行代码时的可预测性,也是优化某些函数的一个有效方法。比如,固定步长或连续访问对CPU来说是可预测的,但非连续访问(例如链表)就是不可预测的。

+
    +
  • cache放置策略
  • +
+

要注意现代缓存是分区的(set associative placement,组相连映射),要注意避免使用 critical stride,这种步长情况下只能利用 cache 的一小部分。

+
+

critical stride,这种类型的步长,指的是内存访问的步长刚好等于 cache 大小。这种情况下,只有少部分 cacheline 被利用。

+
+

写的并发处理逻辑会导致false sharing (#92)

+

了解 CPU 缓存的较低层的 L1、L2 cache 不会在所有核间共享,编写并发处理逻辑时能避免写出一些降低性能的问题,比如伪共享(false sharing)。内存共享只是一种假象。

+

没有考虑指令级的并行 (#93)

+

使用指令级并行(ILP)优化代码的特定部分,以允许CPU尽可能执行更多可以并行执行的指令。识别指令的数据依赖问题(data hazards)是主要步骤之一。

+

不了解数据对齐 (#94)

+

记住Go中基本类型与其自身大小对齐,例如,按降序从大到小重新组织结构体的字段可以形成更紧凑的结构体(减少内存分配,更好的空间局部性),这有助于避免一些常见的错误。

+

不了解 stack vs. heap (#95)

+

了解堆和栈之间的区别是开发人员的核心知识点,特别是要去优化一个Go程序时。栈分配的开销几乎为零,而堆分配则较慢,并且依赖GC来清理内存。

+

不知道如何减少内存分配次数 (API调整, compiler optimizations, and sync.Pool) (#96)

+

减少内存分配次数也是优化Go应用的一个重要方面。这可以通过不同的方式来实现,比如仔细设计API来避免不必要的拷贝,以及使用 sync.Pool 来对待分配对象进行池化。

+

不注意使用内联 (#97)

+

使用快速路径的内联技术来更加有效地减少调用函数的摊销时间。

+

不使用Go问题诊断工具 (#98)

+

了解Go profilng工具、执行时tracer来辅助判断一个应用程序是否正常,以及列出需要优化的部分。

+

不理解GC是如何工作的 (#99)

+

理解如何调优GC能够带来很多收益,例如有助于更高效地处理突增的负载。

+

不了解Docker或者K8S对运行的Go应用的性能影响 (#100)

+

为了避免CPU throttling(CPU限频)问题,当我们在Docker和Kubernetes部署应用时,要知道Go语言对CFS(完全公平调度器)无感知。

+ + + + + + + + + + + + + + + + + +

Comments

+ + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file

Post