[go: up one dir, main page]

Today I Learned

tags


2021/09/05

about go build constraints. Add

//go:build (booleanExprUsingTagNames)

to the top of the conditionally-included file. The boolean expression can be composed of tags (passed via go build -tag <tag>...) or expressions referencing the host or target os and arch.


2021/10/09

That cgo will automagically ship a C.sizeof_YourStruct for every C struct that you can reference as C.YourStruct. This is useful for pointer arithmetic via unsafe.Pointer(uintptr(something) + uintptr(intIndex * C.sizeof_YourStruct)).


2024/03/07

That go mod tidy needs to be followed by go mod vendor to keep ./vendor/modules.txt up-to-date. That go mod vendor only pulls in files that are referenced by your current project.

See https://go.dev/ref/mod#vendoring


2024/04/07

That any function named func init(){ ... } runs on load of a module. See https://go.dev/ref/spec#Package_initialization. See also https://www.digitalocean.com/community/tutorials/understanding-init-in-go.


2024/04/08

That select can only be used to race communications operations (e.g. <- myChan). See https://go.dev/ref/spec#Select_statements


2024/04/22

That Go has labels and can goto label. I’m not entirely sure what the usefulness of these is outside of switch statements, but it’s still cool!


2024/05/04

That a go.work file can point to local “main modules” used for minimum-version selection. go will maintain a separate go.work.sum file with the checksums of the workspace’s dependencies. go work {init,use,edit} manages the work-files. $GOWORK pointing to a file named like *.work can switch between multiple workspace files. See https://go.dev/ref/mod#workspaces; the syntax of *.work files is roughly equivalent to the syntax in go.mod.


That none of the following options ensure unused debug info is not included in rust wasm output:

[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"

Commenting each line and rebuilding with --target=wasm32-unknown-unknown --release resulted in 0% change in output .wasm size. In each experiment, twiggy garbage ./target/wasm*/release/my_lib.wasm reported

 Bytes  │ Size % │ Garbage Item
────────┼────────┼─────────────────────────────────────────
 280220 ┊ 33.30% ┊ custom section '.debug_str'
 181364 ┊ 21.55% ┊ custom section '.debug_info'
 159353 ┊ 18.94% ┊ custom section '.debug_line'
  96881 ┊ 11.51% ┊ custom section '.debug_pubnames'
  87936 ┊ 10.45% ┊ custom section '.debug_ranges'
   2030 ┊  0.24% ┊ custom section '.debug_abbrev'
    342 ┊  0.04% ┊ custom section '.debug_pubtypes'
     67 ┊  0.01% ┊ custom section 'producers'
     28 ┊  0.00% ┊ custom section 'target_features'
 808221 ┊ 96.05% ┊ Σ [9 Total Rows]

I cut those sections out using the 2-year-old recipe from https://github.com/Xe/x/blob/c87eb51e0afe78a958eecaffb83318f91c6f78dd/web/mastosan/README.md:

wasm-opt -oZ ...
wasm-snip \
  --skip-producers-section \
  --snip-rust-panicking-code \
  --snip-rust-fmt-code \
  ...

2024/08/19

That go build ./path/to/main.go disregards other files in the same directory/package. go build ./path/to works fine.


2024/09/24

That you an render code blocks in Go doc comments by indenting the lines of code:

// Add two numbers.
//   this
//   is a
//   code block
func Add(a, b int) int {
  return a + b
}

See https://tip.golang.org/doc/comment#code


2024/10/23

That

An empty go.mod in a directory will cause that directory and all of its subdirectories to be excluded from the top-level Go module.

https://go.dev/wiki/Modules#can-an-additional-gomod-exclude-unnecessary-content-do-modules-have-the-equivalent-of-a-gitignore-file


2024/11/25

That (1) go tries to bake VCS info into go binaries and (2) go still doesn’t understand git worktrees :/ and (3) the easiest way to get go builds to work in a git worktree or bare git repo is to

go env -w GOFLAGS=-buildvcs=false

I’m not sure how global these flags are.


2025/01/30

That you can write golang examples that get run using go test:

func ExampleHello() {
   fmt.Println("hello")
   // Output: hello
}

The naming convention to declare examples for the package, a function F, a type T and method M on type T are:

func Example() { ... }
func ExampleF() { ... }
func ExampleT() { ... }
func ExampleT_M() { ... }

https://pkg.go.dev/testing#hdr-Examples

I feel like this might be able to be combined with doc comments.


2025/02/12

That defer statements are block-scoped:

package main

import "fmt"

func main() {
	defer fmt.Println("end func") // runs at end of function
	fmt.Println("start func")
	{
		defer fmt.Println("end block") // runs at end of block
		fmt.Println("start block")
	}
}
// Output:
// start func
// statt block
// end block
// end func

see https://go.dev/play/p/2OYL2-g1iUE


2025/02/19

How to set build-time variables in go:

go build -ldflags "-X importpath.name=value"

See https://pkg.go.dev/cmd/link


2025/04/25

Apparently, the blessed way to do prettier-style line-wrapping in Go is golines: https://github.com/golang/vscode-go/issues/847#issuecomment-816992225


2025/05/01

That wrapper types preserve type information in switch statements:

package main

import "fmt"

func main() {
	type x string
	y := x("asdf")
	z := any(y)
	switch z.(type) {
	case x:
		fmt.Println("cool")
	case string:
		fmt.Println("uh")
	}
}

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


2025/05/09

Clicking “Debug Test” in VSCode doesn’t use the configuration in launch.json: https://github.com/golang/vscode-go/wiki/debugging#:~:text=the%20%22debug%20test%22%20codelens%20and%20the%20test%20ui%20do%20not%20use%20the%20launch.json%20configuration


2025/06/14

That mobile OS’s often do not define a $HOME variable:

	// On some geese the home directory is not always defined.
	switch runtime.GOOS {
	case "android":
		return "/sdcard", nil
	case "ios":
		return "/", nil
	}

Side note: hats off to the programmer that called the plural of GOOSes “geese”.


2025/06/19

That go’s runtime/debug package keeps track of the version of all modules:

package main

import (
	"fmt"
	"runtime/debug"
)

func main() {
	info, _ := debug.ReadBuildInfo()
	fmt.Printf("info: %#v", info)
}

// info: &debug.BuildInfo{
//   GoVersion: "go1.24.4",
//   Path: "play",
//   Main:debug.Module{
//     Path:"play",
//     Version:"(devel)",
//     Sum:"",
//     Replace:(*debug.Module)(nil),
//   },
//   Deps:[]*debug.Module(nil),
//   Settings:[]debug.BuildSetting{
//     debug.BuildSetting{Key:"-buildmode", Value:"exe"},
//     debug.BuildSetting{Key:"-compiler", Value:"gc"},
//     debug.BuildSetting{Key:"-tags", Value:"faketime"},
//     debug.BuildSetting{Key:"CGO_ENABLED", Value:"0"},
//     debug.BuildSetting{Key:"GOARCH", Value:"amd64"},
//     debug.BuildSetting{Key:"GOOS", Value:"linux"},
//     debug.BuildSetting{Key:"GOAMD64", Value:"v1"}}}

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

See also https://pkg.go.dev/runtime/debug#BuildInfo

I heard about this from https://github.com/charmbracelet/fang#:~:text=automatic%20--version%3A%20set%20it%20to%20the%20build%20info%2C%20or%20a%20version%20of%20your%20choice


2025/09/24

That google’s Go style guide prefers all-same-case acronyms: https://google.github.io/styleguide/go/decisions.html#initialisms


2025/11/01

That gopls can emit UI hints about escape analysis: https://github.com/golang/vscode-go/wiki/settings#uidiagnosticannotations


2025/12/03

When compiling packages, build ignores files that end in ‘_test.go’.

https://pkg.go.dev/cmd/go#:~:text=When%20compiling%20packages%2C%20build%20ignores%20files%20that%20end%20in%20%27%5Ftest%2Ego%27%2EA


2025/12/04

That not using a pointer reciever for a custom error type can cause runtime panics:

If you use non-pointer values and folks do shallow comparisons without an Is method, you may put yourselves in a bad spot if the error type later includes a non-comparable child field (e.g., map[K]V). See https://go.dev/play/p/7fgO0EY5hWm.

https://www.reddit.com/r/golang/comments/1bd1xzf/seeking_advice_on_custom_error_types_value_vs/