Skip to content

Latest commit

 

History

History
566 lines (451 loc) · 26.4 KB

File metadata and controls

566 lines (451 loc) · 26.4 KB

Go programming language Stdlib Proverbs Effective Go rc

  • HOWTO Install by Brew brew install go Homebrew Go package.
  • DOC Package name convention are given lower case, single-word names; there should be no need for underscores or mixedCaps. Naming convention style Effective Go Package names Code Review Package names Go Blog Package names 4282605948
  • DOC Interface name convention use an -er suffix: Reader, Writer, Formatter, CloseNotifier etc. Naming convention style 2453223740
  • HOWTO Environment go env
  • DOC Book "The Go Programming Language" Alan A. A. Donovan, Brian W. Kernighan *
  • HOWTO Install package go get -v ./... Get with all dependencies (exclude test dependencies).
  • HOWTO Install package with test dependencies go get -v -t ./... Get test dependencies.
  • HOWTO Update package go get -u your.tld/path/to/pkg
  • HOWTO Package dependencies list go list -f '{{ .Deps }}' your.tld/path/to/pkg List package dependencies
  • HOWTO Install package go install your.tld/path/to/pkg
  • HOWTO List packages go list ./... List installed packages. 557989519
  • HOWTO Testing go test -v -race -count=1 ./... Run test suite 2319142434 2570645731
  • HOWTO Testing of remote package go test your.tld/path/to/pkg
  • HOWTO Testing without cache go test -count=1 Testing with cache disabled. 4126800382
  • HOWTO VER2 Test benchmark go test -race -count=1 -bench=. -benchmem ./... Run benchmark.
  • HOWTO Test coverage go test -cover ./... 2445429477
  • HOWTO go test -race ./... Test races/race condition/with race detector. 1720623323
  • HOWTO Table test generator
  • HOWTO godef finds function definition go get github.com/rogpeppe/godef && godef -f path/to/file.go 'yourpkgnm.YourFnNm' Find function definition by package and function name.
  • HOWTO Run go run ./...
  • HOWTO Get build ID go tool buildid path/to/your/file Display or update the build ID stored in a Go package or binary.
  • HOWTO Build CGO_ENABLED=0 go build ./... 4169212427 318174330
  • HOWTO Build custom name go build -o path/to/your/executable Build with custom executable name and custom output directory.
  • HOWTO Build without cache go build -a path/to/your/package Build cache invalidation. Invalidate cache. Recompile.
  • HOWTO Runtime Gosched runtime.Gosched() Yields the processor, allowing other goroutines to run. It necessary for cooperative scheduler (кооперативного планировщика) until Go 1.13 and unnecessary for preemptive scheduler (вытесняющего планировщика) starts Go 1.14. 3989277831
  • HOWTO Runtime GC disable GOGC=off 269738468
  • HOWTO Debug FreeOSMemory debug.FreeOSMemory() Forces a garbage collection followed by an attempt to return as much memory to the operating system as possible.
  • HOWTO Reduce binary size while build go build --ldflags "-s -w" path/to/package Minimize binary size.
  • HOWTO Reduce binary size while run go run --ldflags "-s -w" path/to/package Minimize binary size.
  • HOWTO Compile time variables while build go build -ldflags "-X path/to/package.foo=$(git describe --abbrev=0 --tags) -X path/to/package.Bar=$(git rev-parse --short HEAD) -X path/to/package.baz=$(git rev-parse HEAD) -X path/to/package.qux=$(date --utc +%s) -X path/to/package.xyz=$(date --utc +%Y%m%dT%H%M%SZ)" main.go Set some string variable on compile time (for example your some "version").
  • HOWTO Compile time variables while run go run -ldflags "-X path/to/package.foo=123 -X path/to/package.Bar=xyz" ./... Set some string variable on compile time (for example your some "version").
  • HOWTO Cross compilation env GOARCH=arm64 go build
  • HOWTO Code review: 80 characters or not? Code review: N characters line length convention style.
  • HOWTO Code review: Initialism convention Id->ID Url->URL Xml->XML and so on Code review abbreviation style.
  • HOWTO Code review: The named return is good but the naked is bad Convention style. Bare return is bad. 1 2 3
  • HOWTO Type conversion var i int = 42; f float64 = float(i); i := (*int)(nil) Example.
  • HOWTO Type assertion var x interface{} = 42; i := x.(int) Example.
  • DOC Generic type Intro generics since 1.18. When generics. Example[].
  • HOWTO Dave Cheney: "accept interfaces, return structs" 991876724 Jack Lindamood: "A great rule of thumb for Go is accept interfaces, return structs." Cite.
  • HOWTO Non-nil interface type and nil interface value if i, ok := value.source.(fmt.Stringer); ok {; if i == nil || (reflect.ValueOf(i).Kind() == reflect.Ptr && reflect.ValueOf(i).IsNil()) {; return "This is real nil or nil value and non nil type."; }; }
  • HOWTO Print Formatter type Formatter interface {; Format(f State, c rune); } 99610387
  • HOWTO Print Structured logging Printing. Logging. Structured logging blog article
  • HOWTO Print Go-cmp compare values cmp.Equal(your_val1, your_val2) and cmp.Diff(your_val1, your_val2) Printing. Logging. Check equality or difference. ignore := []cmp.Option{cmpopts.IgnoreFields(yourpkg.YourType{}, "YourFld1", "YourFld1")}; if !cmp.Equal(got, want, ignore...) { t.Errorf("\nyour diff:\n%s\ntest: %s", cmp.Diff(want, got, ignore...), "path/to/your_test.go:42") } GitLab uses go-cmp too. Dave Cheney uses cmp too. Testing. Debuging. Compare difference. Pretty print.
  • HOWTO Print Proto equal proto.Equal(your_proto1, your_proto2) Printing. Logging. Check equality. Testing. Debugging. DeepEqual
  • HOWTO Print Proto diff cmp.Diff(your_proto1, your_proto2, protocmp.Transform()) Printing. Logging. Check difference. Testing. Debugging. DeepEqual
  • HOWTO Print Error diff cmp.Diff(your_error1, your_error2, cmpopts.EquateErrors()) Printing. Logging. Check difference. Testing. Debugging.
  • HOWTO Error wrap format join errors.Join(fmt.Errorf("your error: %w", errors.New("foo")), errors.New("bar")) Wrapping. Formatting. Joining. 1238582052 2031092561 2122683529
  • HOWTO Error unwrap and compare func equal(err, err2 error) bool { if err == err2 { return true }; if err := errors.Unwrap(err); err != nil { if ok := equal(err, err2); ok { return true } }; if v, ok := err.(interface{ Unwrap() []error }); ok { for _, err := range v.Unwrap() { if ok := equal(err, err2); ok { return true } } }; return false } The example equal fn compares the wrapped errors recursively. 1121809002.
  • DOC Sentinel is a sentinel error Working with Errors in Go 1.13 Dave Cheney, Sentinel error Error Inspection Draft Design 2018-08-27 Damien Neil
  • DOC Sentinel io.EOF End of input. Common error. 564276647
  • DOC Sentinel os.ErrInvalid fs.ErrInvalid Invalid argument. Common error. 564276647
  • DOC Sentinel os.ErrPermission fs.ErrPermission Permission denied. Common error. 564276647
  • DOC Sentinel os.ErrExist fs.ErrExist File already exists. Common error. 564276647
  • DOC Sentinel os.ErrNotExist fs.ErrNotExist File does not exist. Common error. 564276647
  • DOC Sentinel os.ErrClosed fs.ErrClosed File already closed. Common error. 564276647
  • DOC Sentinel os.ErrNoDeadline File type does not support deadline. Common error. 564276647
  • DOC Sentinel os.ErrDeadlineExceeded I/O timeout. Common error. 564276647
  • DOC Sentinel sql.ErrNoRows No rows in result set. Common error. 564276647
  • DOC Sentinel sql.ErrNoRows No rows in result set. Common error. 564276647
  • DOC Sentinel fs.PathError 1595088486
  • DOC Sentinel fs.PathError if e, ok := err.(*fs.PathError); ok { e.Timeout() } or url.Error if e, ok := err.(*url.Error); ok { e.Timeout() } or in general if e, ok := err.(interface{ Timeout() bool }); ok { e.Timeout() } 1595088486
  • DOC Sentinel url.InvalidHostError is a string error.
  • HOWTO Explicit argument indexes of formatter fmt.Sprintf("%[2]d %[1]d", 11, 22) Position argument. 3426125172
  • HOWTO Create dir or append file if _, err := os.Stat("your.f"); errors.Is(err, os.ErrNotExist) { _ = os.MkdirAll(filepath.Dir("your.f"), os.ModePerm) }; f, _ := os.OpenFile("your.f", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); defer f.Close(); _, _ = f.Write([]byte("Hello, World!")) Create or update/append file after create directory if not exist. 2502395997 3382294025
  • HOWTO Create temp file VER2 dirNm := filepath.Join(os.TempDir(), "your_dir"); _ = os.MkdirAll(dirNm, os.ModePerm); fileNm := filepath.Join(dirNm, "file_nm"+itoa.Uitoa(rand.Uint64())); f, _ := os.Create(fileNm); defer f.Close(); _, _ = f.Write([]byte("foo bar")) Create temporary file.
  • HOWTO Create temp file VER1 f, _ := os.CreateTemp(filepath.Join(os.TempDir(), "your_dir"), "your_prefix_"); defer f.Close(); _, _ = f.Write([]byte("foo")) Create temporary file.
  • HOWTO Create temp dir VER2 _ = os.MkdirAll(filepath.Join(os.TempDir(), "your_dir"), os.ModePerm) Create temporary directory. Function os.MkdirTemp just like DEPRECATED ioutil.TempDir.
  • HOWTO Create temp dir VER1 d, _ := os.MkdirTemp("", "your_prefix_"); defer os.RemoveAll(d) Create temporary directory.
  • HOWTO Read all bytes var bbytes.Buffer; io.Copy(&b r); fmt.Println("%s", b.String()) Function io.Copy just like DEPRECATED ioutil.ReadAll.
  • HOWTO paths, err := filepath.Glob(pattern) Globbing pattern sets of filenames with wildcard characters.
  • HOWTO Flag Kong is a multi command command-line parser an alternative to using flag.FlagSet PROS: Passthrough argument. 3264233233 NOTE: Console. Terminal.
  • HOWTO VER1 Flag Flag set command-line parser allow multi command In case you can't use, for example, Kong. Console. Terminal.
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
$ go run ./...
Hello, World!

HOWTO VER1 Test benchmark run benchmarks

cat go.mod
module yourpkg

require "your.tld/you/yourpkg" v0.0.0
replace "your.tld/you/yourpkg" v0.0.0 => "./"
cat yourpkg.go
package yourpkg

var hello string

func YourInit() { hello = "Hello" }

type T string

func New() T                 { return T(hello) }
func (x *T) YourFn(s string) { *x = T(string(*x)[:len(hello)] + " " + s) }
func (x *T) YourReset()      { *x = T(string(*x)[:len(hello)]) }
cat your_test.go
package yourpkg_test

import (
	"fmt"
	"path/filepath"
	"runtime"
	"testing"

	"your.tld/you/yourpkg"
)

var tests = []struct {
	name string
	line string
	in   string
	out  string
}{
	{name: "test Alice", in: "Alice", out: "Hello Alice", line: line()},
	{name: "test Bob", in: "Bob", out: "Hello Bob", line: line()},
}

func line() string {
	if _, file, line, ok := runtime.Caller(1); ok {
		return fmt.Sprintf("%s:%d", filepath.Base(file), line)
	}
	return "it was not possible to recover file and line number information about function invocations"
}

func TestYourFn(t *testing.T) {
	t.Parallel()

	yourpkg.YourInit()

	for _, tt := range tests {
		tt := tt

		t.Run(tt.line+"/"+tt.name, func(t *testing.T) {
			t.Parallel()

			in := yourpkg.New()
			in.YourFn(tt.in)

			if string(in) != tt.out {
				t.Errorf("\nwant: %s\n got: %s", tt.out, in)
			}
		})
	}
}

func BenchmarkYourFn(b *testing.B) {
	yourpkg.YourInit()

	b.ResetTimer()
	b.ReportAllocs()

	for _, tt := range tests {
		b.Run(tt.name, func(b *testing.B) {
			in := yourpkg.New()

			for i := 0; i < b.N; i++ {
				in.YourFn(tt.in)
			}

			b.StopTimer()
			in.YourReset()
			b.StartTimer()
		})
	}
}
go test -race -count=1 -bench=. -benchmem ./...

HOWTO Test: fstest: Mocking relative path of file system 3825108315 1746671570 2434259655 1056894504

package main

import "testing/fstest"

func main() {
   testFS := fstest.MapFS{
      "relative/path/to.file": {
         Data: []byte("Hello, world!"),
      },
   }
   p, err := testFS.ReadFile("relative/path/to.file")
   if err != nil {
      panic(err)
   }
   println(string(p) == "Hello, world!")
}

DOC Code review bad/meaningless package name convention style

  1. https://go.dev/doc/effective_go#package-names>
  2. https://go.dev/blog/package-names#bad-package-names
  3. https://github.com/golang/go/wiki/CodeReviewComments#package-names

Bad package name examples: util, common and misc

TROUBLESHOOTING Time type unification disclaimer (wall/civil/monotonic/absolute)

HOWTO Multiply duration

fmt.Println(time.Duration(float64(42*time.Second) * 1.23))

HOWTO Duration growth logarithmically a-la exponential/expotential backoff

for i := 1; i < 100500; i++ {
    d := time.Duration(float64(42*time.Second) * (1 + math.Log(float64(i))))
    if d > 3*time.Minute {
        d = 3 * time.Minute
    }
    time.Sleep(d)
}
timer := time.NewTimer(time.Second)
timer.Stop()
select {
case <-timer.C:
default:
}
timer.Reset(d)
import "google.golang.org/protobuf/internal/strs"

strs.GoCamelCase("text_2") // Text_2
strs.GoCamelCase("text2")  // Text2

HOWTO Regexp match string

regexp.MustCompile("^[a-z]+$").MatchString("foo")

HOWTO Regexp multiline/new line

regexp.MustCompile("(?m)^[a-z]*$\n[a-z]*").MatchString("foo\nxyz")

HOWTO Regexp replacement variable/submatch/capturing group

regexp.MustCompile("(.{3})").ReplaceAll([]byte("foo3"), []byte("${1}12"))

HOWTO Three-index/primary/full slice

For examle a[low:high:max] or a[0:0:0] or a[:0:0]

HOWTO Copy slice

  1. b := append(a[:0:0], a...) is the same as b = append([]T(nil), a...)
  2. b := make([]T, len(a)) and then copy(b, a)
  3. b := append(make([]T, 0, len(a)), a...)

HOWTO Compare byte slices

bytes.Compare returns 0 if a == b, -1 if a < b, and +1 if a > b

bytes.Compare([]byte{1,2}, []byte{3,4})

HOWTO Compare byte arrays

a1, a2 := [2]byte{1, 2}, [2]byte{3, 4}
fmt.Println(bytes.Compare(a1[:], a2[:]))

HOWTO /dev/null io.Copy(io.Discard, strings.NewReader("Hello, World!"))

DOC Language/locale

HOWTO Copy/clone/duplicate struct to struct of another type

https://pkg.go.dev/testing/fstest

package main

import "github.com/jinzhu/copier"

type T1 struct {
    Name string
}

type T2 struct {
    Name string
    Age  int
}

func main() {
    t1 := T1{Name: "John Doe"}
    t2 := T2{Age: 42}
    if err := copier.Copy(&t2, &t1); err != nil {
        panic(err)
    }
}

DOC Context

  1. context.Context
  2. net.Context Is a former beta version.

HOWTO VER2 Flag Flag set Multi command and Multi flag.FlagSet in case you can't use, for example, Kong. Console. Terminal.

package main

import (
	"fmt"
	"flag"
	"os"
)

func main() {
	switch os.Args[1] {
	case "foo":
		set := flag.NewFlagSet("foo", flag.ExitOnError)
		bar := set.Bool("bar", false, "")
		_ = set.Parse(os.Args[2:])
		fmt.Println(*bar)
	case "xyz":
		set := flag.NewFlagSet("xyz", flag.ExitOnError)
		baz := set.Bool("baz", false, "")
		_ = set.Parse(os.Args[2:])
		fmt.Println(*baz)
	}

HOWTO Query string key-value pairs

Query string of Uniform Resource Identifier URI encoded in application/x-www-form-urlencoded algorithm is a sorted list of name-value pairs implemented by net/url package encodes the values into URL encoded form sorted by key.

v := url.Values{}
v.Add("3", "xyz"); v.Add("1", "foo"); v.Add("2", "bar")
fmt.Println(v.Encode()) // 1=foo&2=bar&3=xyz

https://go.dev/play/p/gCRFEuO4CkC

DOC JSON marshaling of map fields in sorted order

The Go Stdlib already preserves sorted order of map keys:

The map keys are sorted

HOWTO HTTP multipart/form-data POST of file by local path * 1102513506 1351755388

curl -X POST "https://your.tld/your/path" -H "Content-Type: multipart/form-data" -F "file=@/etc/hostname;filename=your.file;type=*/*" --basic --user your_user:your_password
package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"path/filepath"
)

func main() {
	form := new(bytes.Buffer)
	writer := multipart.NewWriter(form)
	fw, err := writer.CreateFormFile("file", filepath.Base("your.file"))
	if err != nil {
		log.Fatal(err)
	}

	fd, err := os.Open("/etc/hostname")
	if err != nil {
		log.Fatal(err)
	}
	defer fd.Close()

	_, err = io.Copy(fw, fd)
	if err != nil {
		log.Fatal(err)
	}

	writer.Close()

	client := &http.Client{}
	req, err := http.NewRequest("POST", "https://your.tld/your/path", form)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("Content-Type", writer.FormDataContentType())
	req.SetBasicAuth("your_user", "your_password")

	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	bodyText, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s\n", bodyText)
}