tags
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.
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)).
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
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.
That select can only be used to race communications operations (e.g. <- myChan).
See https://go.dev/ref/spec#Select_statements
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!
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 \
...
That go build ./path/to/main.go disregards other files in the same directory/package.
go build ./path/to works fine.
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
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
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.
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.
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
How to set build-time variables in go:
go build -ldflags "-X importpath.name=value"
See https://pkg.go.dev/cmd/link
Apparently, the blessed way to do prettier-style line-wrapping in Go is golines:
https://github.com/golang/vscode-go/issues/847#issuecomment-816992225
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
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
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”.
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
That google’s Go style guide prefers all-same-case acronyms: https://google.github.io/styleguide/go/decisions.html#initialisms
That gopls can emit UI hints about escape analysis: https://github.com/golang/vscode-go/wiki/settings#uidiagnosticannotations
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
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/