Home · RSS · E-Mail · GitHub · GitLab · Twitter · Mastodon

Go bits

first published:

» VS Code debugging

Add the following to your user settings (or just the configurations object inside the .vscode/launch.json file in the root of your project) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
  "launch": {
    "configurations": [
      {
        "name": "Go",
        "type": "go",
        "request": "launch",
        "mode": "auto",
        "program": "${fileDirname}",
        "env": {
          "TEST_ENV": "TEST_VALUE",
        },
        "args": [],
        "showLog": true,
      }
    ]
  }

The debugging needs to be started from a file of the main package, except when you debug a _test.go file.

» Upgrade all dependencies

1
go get -t -u ./...

Reference https://golang.cafe/blog/how-to-upgrade-golang-dependencies.html

» defer and log.Fatal/os.Exit

defer calls are not executed when os.Exit() is called. So, it’s also not executed on the various log.Fatal() calls, as those methods call os.Exit().

» io.Reader and errors

The io.Reader interface is quite an exception in the Go ecosystem as it may return some succesfully read bytes together with an error. Thus, you can’t just handle the error as you would normally do, you may also have to check if bytes were read in the same call.

See the documentation https://pkg.go.dev/io#Reader

When Read encounters an error or end-of-file condition after successfully reading n > 0 bytes, it returns the number of bytes read. It may return the (non-nil) error from the same call or return the error (and n == 0) from a subsequent call.

» TestMain()

As the name says, it’s an own main function for the tests. When you have several TestMain() functions in your project and import a package, the globals of the given package are not shared between the TestMain().

» os.Exit

TestMain() needs to call os.Exit(m.Run()) at the end of the function, otherwise it will always succeed.

Reference: Docs

» Versions > 1

When you create a new release (e.g. a git tag) with a version bump from 1 to 2, you have to adjust the module in the go.mod file and add a /v2 suffix. Of course, also when there is any other major version bump >1. When you don’t do this, it will become a problem when importing your new package somewhere else.

» Make sure an interface is implemented

To make sure that your implementation satisfies a specific interface, you can add the following code:

1
var _ SomeInterface = &MyImplementation{}

When your implementation does not satisfy the interface, the compiler will complain.

» Concurrency Guard

Limit the amount of specific Goroutines.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
	maxGoroutines := 10
	guard := make(chan struct{}, maxGoroutines)

	for i := 0; i < 100; i++ {
		guard <- struct{}{} // would block if guard channel is already filled
		go func(n int) {
			defer func() { <-guard }()
			fmt.Println("doing work on", i)
		}(i)
	}
}

The above code will not wait until all Goroutines are finished, so it’s mainly useful in endless loops. To fix this behavior and wait until the end of processing all Goroutines, add a WaitGroup:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
	"fmt"
	"sync"
)

func main() {
	maxGoroutines := 10
	guard := make(chan struct{}, maxGoroutines)
	wg := sync.WaitGroup{}

	wg.Add(100)
	for i := 0; i < 100; i++ {
		guard <- struct{}{} // would block if guard channel is already filled
		go func(n int) {
			defer func() {
				<-guard
				wg.Done()
			}()
			fmt.Println("doing work on", i)
		}(i)
	}
	wg.Wait()
}

Reference: artyom




Home · RSS · E-Mail · GitHub · GitLab · Twitter · Mastodon