This cheat sheet is based on Go 1.26.
Table of Contents
- Program structure
- Variables, constants, and types
- Control flow
- Functions
- Arrays, slices, and maps
- Structs, methods, and tags
- Interfaces
- Errors
- Goroutines, channels, and synchronization
- Context
- Generics
- Iterators
- Testing
- Standard-library quick reference
- Tips and common mistakes
- Modules and packages
- Further reading
Program structure
package main // Every executable must be in package main.
import (
"fmt"
"os"
)
func main() {
fmt.Println("Hello, Go 1.26")
os.Exit(0)
}
Enter fullscreen mode Exit fullscreen mode
- Every Go source file starts with a
packagedeclaration. - An executable program uses
package mainand amainfunction. - Group standard-library and third-party imports separately.
Variables, constants, and types
Variable declarations and inference
// var can infer the type from an initializer.
var name = "Gopher" // string
var age int // No initializer: specify a type. Zero value: 0.
var id int64 = 42 // Use an explicit type when int is not what you want.
// Short declaration: inside functions only.
city := "Tokyo"
// Declare multiple variables in a block.
var (
x, y int = 1, 2
msg string
)
Enter fullscreen mode Exit fullscreen mode
Use var name = "Gopher" when an initializer makes the type clear. Without an initializer, write the type explicitly. A := declaration is only valid inside a function, and at least one name on its left side must be new.
Types and zero values
Category Types Zero value Numbers integers, floats, complex numbers0 (0 + 0i for complex)
Strings
string
""
Booleans
bool
false
Arrays
[N]T
every element is T‘s zero value
Structs
struct
every field has its zero value
Reference-containing types
pointers, slices, maps, channels, functions, interfaces
nil
Defined types
e.g. type UserID int
the underlying type’s zero value
Category
Types
Notes
Boolean
bool
true or false
String
string
immutable sequence of bytes (usually UTF-8, but invalid UTF-8 is allowed)
Signed integers
int, int8, int16, int32, int64
int is platform-sized (32 or 64 bit)
Unsigned integers
uint, uint8, uint16, uint32, uint64, uintptr
uintptr holds a pointer’s raw bits
Aliases
byte, rune
aliases for uint8 and int32
Floating point
float32, float64
IEEE 754
Complex
complex64, complex128
float32 or float64 real and imaginary parts
var i8 int8 = 127 // -128 to 127
var f64 float64 = 3.141592653589793
var c complex128 = 1 + 2i
s := "こんにちは"
runes := []rune(s) // Unicode code points
bytes := []byte(s) // raw bytes
// rune = int32 alias for a Unicode code point
var ch rune = '🐹'
Enter fullscreen mode Exit fullscreen mode
Conversion and constants
i := 42
f := float64(i) // Conversions are always explicit; no implicit coercion.
text := fmt.Sprintf("%d", i) // Number to string.
n, err := strconv.Atoi("123") // String to number.
const Pi = 3.14159
// iota: auto-incremented integer constant.
type Weekday int
const (
Sunday Weekday = iota // 0
Monday // 1
Tuesday // 2
)
// Bit-flag pattern.
type Permission uint
const (
Read Permission = 1 << iota // 1
Write // 2
Execute // 4
)
Enter fullscreen mode Exit fullscreen mode
Pointers
x := 10
p := &x // Take the address of x.
*p = 20 // Dereference the pointer.
fmt.Println(x) // 20
p1 := new(int) // *int; the pointed-to value is 0.
// Go 1.26+: new accepts an expression as well as a type.
// This does not compile with Go 1.25 or earlier.
p2 := new(42) // *int; the pointed-to value is 42.
p3 := new(Person{Name: "Alice"}) // *Person initialized with a literal.
Enter fullscreen mode Exit fullscreen mode
Control flow
if
// An initialization statement is scoped to this if/else chain.
if err := doSomething(); err != nil {
return fmt.Errorf("do something: %w", err)
}
if x > 0 {
fmt.Println("positive")
} else if x < 0 {
fmt.Println("negative")
} else {
fmt.Println("zero")
}
Enter fullscreen mode Exit fullscreen mode
for
// C-style loop.
for i := 0; i < 5; i++ {
}
// Go has no while keyword; use for instead.
for n > 0 {
n--
}
// Infinite loop.
for {
if done {
break
}
}
// range returns an index and a value for slices and arrays.
for i, v := range []int{10, 20, 30} {
fmt.Println(i, v) // 0 10 / 1 20 / 2 30
}
// Discard the index.
for _, v := range slice {
}
// Strings range over Unicode code points (runes), not bytes.
for i, r := range "日本語" {
fmt.Printf("%d: %c\n", i, r)
}
// Maps.
for k, v := range m {
fmt.Println(k, v) // Iteration order is unspecified.
}
Enter fullscreen mode Exit fullscreen mode
switch
// Expression switch (fallthrough requires an explicit keyword).
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("macOS")
case "linux":
fmt.Println("Linux")
default:
fmt.Println("Other")
}
// Condition-less switch is a cleaner alternative to a long if-else chain.
switch {
case x > 100:
fmt.Println("big")
case x > 10:
fmt.Println("medium")
default:
fmt.Println("small")
}
// Type switch: identify the dynamic type of an interface value.
func describe(i any) {
switch v := i.(type) {
case int:
fmt.Printf("int: %d\n", v)
case string:
fmt.Printf("string: %q\n", v)
default:
fmt.Printf("unknown: %T\n", v)
}
}
Enter fullscreen mode Exit fullscreen mode
defer
defer schedules a call to run just before the surrounding function returns. Use it for cleanup: closing files, response bodies, mutexes, and contexts. Deferred calls run in LIFO order.
func writeFile(path string) error {
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close() // Runs when writeFile returns, regardless of how.
defer fmt.Println("third")
defer fmt.Println("second")
defer fmt.Println("first")
// Prints: first, second, third.
return nil
}
Enter fullscreen mode Exit fullscreen mode
Note
Deferred call arguments are evaluated whendeferis encountered. In a long-running loop, a deferred cleanup does not run until the function returns, so resources accumulate. Close them promptly or move the loop body into a helper function.
Functions
Basic
// Multiple return values.
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
result, err := divide(10, 3)
// Named return values (useful as documentation for short functions).
func minMax(nums []int) (min, max int) {
min, max = nums[0], nums[0]
for _, n := range nums[1:] {
if n < min {
min = n
}
if n > max {
max = n
}
}
return // Naked return.
}
Enter fullscreen mode Exit fullscreen mode
Variadic functions
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
sum(1, 2, 3)
nums := []int{1, 2, 3}
sum(nums...) // Spread a slice into variadic arguments.
Enter fullscreen mode Exit fullscreen mode
First-class functions and closures
Functions are values in Go: you can assign them to variables, pass them as arguments, and return them. A closure captures variables from its surrounding scope and keeps them alive after the outer function returns.
// Assign a function to a variable.
add := func(a, b int) int { return a + b }
// Pass a function as an argument.
apply := func(f func(int, int) int, x, y int) int {
return f(x, y)
}
fmt.Println(apply(add, 3, 4)) // 7
// This closure captures n.
func counter() func() int {
n := 0
return func() int {
n++
return n
}
}
c := counter()
c() // 1
c() // 2
Enter fullscreen mode Exit fullscreen mode
init
An init function runs automatically while a package is initialized, before main. A package may have more than one init function. Prefer explicit initialization in main for ordinary application setup; reserve init for package-level registration.
// Runs automatically at package initialization.
func init() {
// Register a driver or handler.
}
Enter fullscreen mode Exit fullscreen mode
Arrays, slices, and maps
Memory layout
Array (fixed length, value type)
┌───┬───┬───┬───┬───┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ ← contiguous memory (placement decided by the compiler)
└───┴───┴───┴───┴───┘
Slice (variable-length view)
┌────────┬──────┬──────┐
│ ptr │ len │ cap │ ← 3-word slice header
└────┬───┴──────┴──────┘
↓
┌───┬───┬───┬───┬───┬───┐
│ 0 │ 1 │ 2 │ 3 │ │ │ ← underlying array
└───┴───┴───┴───┴───┴───┘
↑ ↑
len=4 cap=6
Note: actual placement (stack/heap) is decided by escape analysis.
Enter fullscreen mode Exit fullscreen mode
Arrays
Arrays have a fixed length, and that length is part of their type: [3]int and [4]int are different types. Arrays are values, so assigning or passing one copies all its elements.
var a [3]int // [0 0 0]
b := [3]int{1, 2, 3}
c := [...]int{4, 5, 6} // Length inferred from the literal.
// Arrays are copied by value.
d := b
d[0] = 99 // b is unchanged.
Enter fullscreen mode Exit fullscreen mode
Slices
A slice is a variable-length view over part or all of an underlying array. Unlike an array, its length is not part of its type. Assigning a slice copies only its header, so two slices may share elements.
// Create a slice.
s := []int{1, 2, 3}
s2 := make([]int, 5) // len=5, cap=5
s3 := make([]int, 3, 10) // len=3, cap=10
// append may allocate a new backing array when cap is exceeded.
s = append(s, 4, 5)
s = append(s, []int{6, 7}...)
// Slicing shares the backing array.
sub := s[1:3] // s[1] and s[2]; shares s's array.
limited := s[1:3:3] // Third index limits the resulting cap.
// copy produces an independent slice.
clone := make([]int, len(s))
copy(clone, s)
// Delete element at index i while preserving order.
i := 2
s = append(s[:i], s[i+1:]...)
fmt.Println(len(s), cap(s))
Enter fullscreen mode Exit fullscreen mode
Caution
A subslice shares its backing array with the original. Mutating one can mutate the other. Usecopywhen an independent slice is required.
Maps
Maps associate keys with values. Iteration order is unspecified. Keys must be comparable; slices, maps, and functions cannot be map keys.
// Create a map.
m := map[string]int{"a": 1, "b": 2}
m2 := make(map[string]int)
m["c"] = 3
delete(m, "a") // delete is a built-in function.
// Check for key existence.
v, ok := m["missing"] // ok is false when the key is absent.
if ok {
fmt.Println(v)
}
// Iteration order is unspecified.
for k, v := range m {
fmt.Println(k, v)
}
Enter fullscreen mode Exit fullscreen mode
Caution
The zero value of a map isnil. Reading from a nil map is safe, but writing to one panics. Initialize it withmakeor a map literal first.
Structs, methods, and tags
Structs and embedding
type Person struct {
Name string // Exported: accessible from other packages.
Age int
addr string // Unexported: package-private.
}
// Named-field initialization (preferred).
p1 := Person{Name: "Alice", Age: 30}
p2 := new(Person) // *Person; all fields are zero values.
// Embedding promotes fields and methods.
type Employee struct {
Person // Fields and methods of Person are promoted.
Department string
}
e := Employee{Person: Person{Name: "Carol", Age: 28}, Department: "Eng"}
fmt.Println(e.Name) // Transparent access to Person.Name.
Enter fullscreen mode Exit fullscreen mode
Methods
// Value receiver: p is a copy of the caller.
func (p Person) Greet() string {
return "Hi, I'm " + p.Name
}
// Pointer receiver: can modify the original Person.
func (p *Person) Birthday() {
p.Age++
}
p := Person{Name: "Dave", Age: 20}
p.Birthday() // Go automatically passes &p.
fmt.Println(p.Age) // 21
Enter fullscreen mode Exit fullscreen mode
When to use pointer receivers
- The method needs to modify the receiver → pointer receiver.
- The struct is large (avoid copy overhead) → pointer receiver.
- Otherwise, either works.
Keep receiver kinds consistent across all methods of one type.
Struct tags
Struct tags are metadata attached to fields. The language does not interpret them; packages such as encoding/json and ORMs read them through reflection.
type User struct {
ID int `json:"id" db:"user_id"`
Email string `json:"email,omitempty"` // Omitted from JSON when empty.
}
// encoding/json reads tags through reflection.
data, _ := json.Marshal(User{ID: 1, Email: ""})
// → {"id":1} (Email omitted by omitempty)
Enter fullscreen mode Exit fullscreen mode
Interfaces
An interface defines a set of methods. A type satisfies an interface implicitly — no implements keyword. This lets code depend on behavior rather than a concrete type.
interface value layout (the internal representation is not part of the spec)
var s Shape = Circle{Radius: 5}
s (interface value)
┌──────────────┬──────────────┐
│ type info │ value/ptr │
│ (Circle) │ {Radius:5} │
└──────────────┴──────────────┘
If either part is nil while the other is not, the interface value itself is not nil.
(See nil interface trap in Tips.)
Enter fullscreen mode Exit fullscreen mode
type Shape interface {
Area() float64
Perimeter() float64
}
type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius }
func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius }
// Circle implicitly satisfies Shape.
var s Shape = Circle{Radius: 5}
fmt.Println(s.Area())
Enter fullscreen mode Exit fullscreen mode
Type assertions and type switches
// Safe type assertion; the single-result form panics on failure.
c, ok := s.(Circle)
if ok {
fmt.Println("radius:", c.Radius)
}
// any is an alias for interface{} (Go 1.18+).
func printAny(v any) {
fmt.Printf("%T: %v\n", v, v)
}
Enter fullscreen mode Exit fullscreen mode
Small interfaces used everywhere
// io.Reader and io.Writer are the most widely used interfaces in the stdlib.
type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }
// Implementing fmt.Stringer controls how fmt.Println prints a value.
type Stringer interface { String() string }
func (p Person) String() string {
return fmt.Sprintf("%s (%d)", p.Name, p.Age)
}
fmt.Println(Person{Name: "Eve", Age: 22}) // Eve (22)
Enter fullscreen mode Exit fullscreen mode
Errors
// error is a built-in interface: type error interface { Error() string }
f, err := os.Open("file.txt")
if err != nil {
return fmt.Errorf("open file: %w", err) // %w wraps err for errors.Is/As.
}
defer f.Close()
Enter fullscreen mode Exit fullscreen mode
Custom errors
type NotFoundError struct {
Resource string
ID int
}
func (e *NotFoundError) Error() string {
return fmt.Sprintf("%s (id=%d) not found", e.Resource, e.ID)
}
func findUser(id int) (*User, error) {
if id <= 0 {
return nil, &NotFoundError{Resource: "user", ID: id}
}
// ...
return nil, nil
}
Enter fullscreen mode Exit fullscreen mode
errors package
import "errors"
// Sentinel error for errors.Is comparisons.
var ErrNotFound = errors.New("not found")
func fetchUser(id int) (*User, error) {
if id <= 0 {
return nil, fmt.Errorf("fetchUser: %w", ErrNotFound) // Wrap with %w.
}
return nil, nil
}
_, err := fetchUser(-1) // fetchUser returns (*User, error); discard the value.
// errors.Is traverses the error chain to find a matching sentinel.
if errors.Is(err, ErrNotFound) {
fmt.Println("not found")
}
// errors.As traverses the chain and extracts a value of the target type.
func findItem(id int) error {
return fmt.Errorf("findItem: %w", &NotFoundError{Resource: "item", ID: id})
}
var nfe *NotFoundError
if errors.As(findItem(42), &nfe) {
fmt.Println("resource:", nfe.Resource) // "item"
}
// Go 1.26+: a type-safe generic alternative to errors.As.
if nfe, ok := errors.AsType[*NotFoundError](findItem(42)); ok {
fmt.Println("resource:", nfe.Resource) // "item"
}
// errors.Join combines multiple errors into one (Go 1.20+).
err1 := errors.New("err1")
err2 := errors.New("err2")
combined := errors.Join(err1, err2)
Enter fullscreen mode Exit fullscreen mode
panic and recover
Use panic for broken program invariants or truly unrecoverable situations; return error for expected failures. recover only works when called directly from a deferred function.
func mustPositive(n int) int {
if n <= 0 {
panic(fmt.Sprintf("must be positive, got %d", n))
}
return n
}
func safeDiv(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered: %v", r)
}
}()
return a / b, nil
}
Enter fullscreen mode Exit fullscreen mode
Goroutines, channels, and synchronization
GMP model (Go runtime scheduler)
G = Goroutine (lightweight thread; initial stack ~2 KB, grows as needed)
M = OS thread
P = Processor (controlled by GOMAXPROCS)
P1 P2
┌────────────┐ ┌────────────┐
│ G1 running │ │ G3 running │
│ ┌────────┐ │ │ ┌────────┐ │
│ │ G2 │ │ │ │ G4 │ │ ← local run queue
│ │ G5 │ │ └────────────┘
└────────────┘
M1 M2 ← OS threads
Enter fullscreen mode Exit fullscreen mode
Goroutines
// go keyword: start a function concurrently.
go func() {
fmt.Println("goroutine")
}()
// Wait for goroutines to finish.
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
fmt.Println(n)
}(i) // Pass i as an argument to avoid closure capture issues.
}
wg.Wait()
Enter fullscreen mode Exit fullscreen mode
Channels
ch := make(chan int) // Unbuffered: send blocks until a receiver is ready.
bch := make(chan int, 10) // Buffered: send blocks only when the buffer is full.
// Unbuffered: send and receive must be in separate goroutines.
go func() { ch <- 42 }()
v := <-ch
// Buffered: send and receive can be in the same goroutine within capacity.
bch <- 1
bch <- 2
fmt.Println(<-bch, <-bch) // 1 2
// Receiving from a closed, drained channel returns (zero, false).
v, ok := <-ch // ok is false when the channel is closed and empty.
Enter fullscreen mode Exit fullscreen mode
// range receives values until the channel is closed and drained.
ch2 := make(chan int, 3)
ch2 <- 1; ch2 <- 2; ch2 <- 3
close(ch2) // The sender closes the channel.
for v := range ch2 {
fmt.Println(v) // 1, 2, 3
}
Enter fullscreen mode Exit fullscreen mode
Channel direction
// Directional types make intent explicit and catch mistakes at compile time.
func producer(ch chan<- int) { // Send-only.
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
func consumer(ch <-chan int) { // Receive-only.
for v := range ch {
fmt.Println(v)
}
}
ch := make(chan int)
go producer(ch)
consumer(ch)
Enter fullscreen mode Exit fullscreen mode
select
// select waits on multiple channel operations simultaneously.
// It picks one ready case at random when several are ready.
select {
case v := <-ch1:
fmt.Println("ch1:", v)
case v := <-ch2:
fmt.Println("ch2:", v)
case ch3 <- data:
fmt.Println("sent to ch3")
case <-time.After(1 * time.Second):
fmt.Println("timeout")
default:
fmt.Println("no channel ready") // Non-blocking.
}
Enter fullscreen mode Exit fullscreen mode
sync and atomic
// Mutex: protect shared state from concurrent access.
var mu sync.Mutex
var count int
mu.Lock()
count++
mu.Unlock()
// RWMutex: more efficient when reads are much more frequent than writes.
var rw sync.RWMutex
rw.RLock() // Multiple goroutines can read simultaneously.
// ... read ...
rw.RUnlock()
// Once: run an initialization function exactly once.
var once sync.Once
once.Do(func() { /* called only the first time */ })
// atomic: low-overhead operations for simple counters and flags.
var n atomic.Int64
n.Add(1)
fmt.Println(n.Load())
Enter fullscreen mode Exit fullscreen mode
Avoid goroutine leaks
When a goroutine sends on a channel, always give it a way to stop if the receiver exits early.
select {
case ch <- result:
case <-ctx.Done(): // Exit when the caller cancels.
return
}
Enter fullscreen mode Exit fullscreen mode
Context
Context tree
context.Background()
└── WithCancel → ctx, cancel
└── WithTimeout(ctx, 5s) → ctx2, cancel2
└── WithValue(ctx2, key, val) → ctx3
↓
Cancellation and deadlines propagate to child contexts.
Enter fullscreen mode Exit fullscreen mode
Use context.Context to propagate deadlines, cancellation, and request-scoped metadata. Call the returned cancel function even when a timeout will eventually fire.
import "context"
// Timeout context.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() // Always call cancel to release resources.
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://example.com", nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("timeout")
}
return err
}
defer resp.Body.Close()
// Cancellable context.
ctx2, cancel2 := context.WithCancel(context.Background())
go func() {
<-ctx2.Done() // Block until cancelled.
fmt.Println("cancelled:", ctx2.Err())
}()
cancel2()
// context.WithValue carries request-scoped metadata.
// Use it only for small amounts of data; never as a substitute for function parameters.
type ctxKey string
const userKey ctxKey = "user"
ctx3 := context.WithValue(context.Background(), userKey, "alice")
user, ok := ctx3.Value(userKey).(string) // Type-assert with an ok check.
Enter fullscreen mode Exit fullscreen mode
Generics
Go 1.18 introduced type parameters for reusable, type-safe code.
Type parameters
// T and U can be any type.
func Map[T, U any](s []T, f func(T) U) []U {
result := make([]U, len(s))
for i, v := range s {
result[i] = f(v)
}
return result
}
doubled := Map([]int{1, 2, 3}, func(n int) int { return n * 2 })
// [2 4 6]
Enter fullscreen mode Exit fullscreen mode
Type constraints
import "cmp"
// cmp.Ordered allows <, >, == (int, float64, string, etc.).
func Min[T cmp.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
Min(3, 5) // 3 (int)
Min(3.14, 2.71) // 2.71 (float64)
Min("b", "a") // "a" (string)
// Custom constraint with ~ for underlying types.
type Number interface {
~int | ~int64 | ~float64
}
func Sum[T Number](nums []T) T {
var total T
for _, n := range nums {
total += n
}
return total
}
Enter fullscreen mode Exit fullscreen mode
Generic types
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(v T) {
s.items = append(s.items, v)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false // Return the zero value of T.
}
v := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return v, true
}
s := Stack[string]{}
s.Push("Go")
s.Push("Generics")
v, _ := s.Pop() // "Generics"
Enter fullscreen mode Exit fullscreen mode
Iterators
Since Go 1.23, a function returning iter.Seq can be ranged over directly. When the caller breaks early, yield returns false so the producer can stop.
import "iter"
// Backward yields n-1 through 0.
func Backward(n int) iter.Seq[int] {
return func(yield func(int) bool) {
for i := n - 1; i >= 0; i-- {
if !yield(i) { // Caller broke out of the loop.
return
}
}
}
}
for i := range Backward(3) {
fmt.Println(i) // 2, 1, 0
}
// maps.Keys returns an iter.Seq; slices.Collect converts it to a slice.
keys := slices.Collect(maps.Keys(m))
Enter fullscreen mode Exit fullscreen mode
Testing
// Place tests in xxx_test.go files.
package mypackage_test
import (
"testing"
"github.com/yourname/yourproject/mypackage"
)
func TestAdd(t *testing.T) {
got := mypackage.Add(2, 3)
if got != 5 {
t.Errorf("Add(2, 3) = %d; want 5", got)
}
}
// Table-driven tests are the idiomatic Go pattern.
func TestDivide(t *testing.T) {
tests := []struct {
name string
a, b float64
want float64
wantErr bool
}{
{"normal", 10, 2, 5, false},
{"division by zero", 10, 0, 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := mypackage.Divide(tt.a, tt.b)
if (err != nil) != tt.wantErr {
t.Errorf("unexpected error: %v", err)
}
if got != tt.want {
t.Errorf("got %v, want %v", got, tt.want)
}
})
}
}
// Benchmark: run with go test -bench=.
func BenchmarkAdd(b *testing.B) {
b.ReportAllocs() // Report memory allocations per operation.
for i := 0; i < b.N; i++ {
mypackage.Add(2, 3)
}
}
Enter fullscreen mode Exit fullscreen mode
Test helpers
// t.Helper makes failures report the caller's line, not the helper's.
func requireNoError(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatal(err)
}
}
func TestWriteFile(t *testing.T) {
t.Setenv("APP_ENV", "test") // Restored automatically after the test.
dir := t.TempDir() // Deleted automatically after the test.
t.Cleanup(func() {
// Register arbitrary cleanup: reset mocks, close clients, etc.
})
path := filepath.Join(dir, "output.txt")
requireNoError(t, os.WriteFile(path, []byte("hello"), 0o644))
}
Enter fullscreen mode Exit fullscreen mode
go test ./... # Run all tests.
go test -race ./... # Detect data races.
go test -cover ./... # Show coverage.
Enter fullscreen mode Exit fullscreen mode
Standard-library quick reference
fmt
// Output.
fmt.Print("no newline")
fmt.Println("with newline")
fmt.Printf("formatted %s %d %v\n", "str", 42, true)
fmt.Fprintf(os.Stderr, "error: %v\n", err)
// Build strings.
s := fmt.Sprintf("Hello, %s!", name)
msg := fmt.Errorf("wrap: %w", err)
// Common format verbs.
// %v default %+v struct with field names %#v Go syntax
// %T type name %d decimal integer %f floating-point
// %s string %q quoted string %x hexadecimal
// %p pointer address %b binary %e exponent
Enter fullscreen mode Exit fullscreen mode
strings
import "strings"
strings.Contains("Hello", "ell") // true
strings.HasPrefix("Hello", "He") // true
strings.HasSuffix("Hello", "lo") // true
strings.Count("cheese", "e") // 3
strings.Index("Hello", "ll") // 2
strings.Replace("oink oink", "oink", "moo", 1) // "moo oink"
strings.ReplaceAll("oink oink", "oink", "moo") // "moo moo"
strings.ToUpper("hello") // "HELLO"
strings.ToLower("HELLO") // "hello"
strings.TrimSpace(" hello ") // "hello"
strings.Trim("##hello##", "#") // "hello"
strings.Split("a,b,c", ",") // ["a" "b" "c"]
strings.Join([]string{"a", "b"}, "-") // "a-b"
strings.Fields("a b\tc") // ["a" "b" "c"]
// strings.Builder is efficient for repeated concatenation in a loop.
// A plain + is fine for a small number of concatenations.
var sb strings.Builder
for i := 0; i < 5; i++ {
fmt.Fprintf(&sb, "%d", i)
}
sb.String() // "01234"
Enter fullscreen mode Exit fullscreen mode
strconv
import "strconv"
strconv.Itoa(42) // "42"
strconv.Atoi("42") // 42, nil
strconv.ParseFloat("3.14", 64) // 3.14, nil
strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"
strconv.ParseBool("true") // true, nil
strconv.FormatBool(true) // "true"
Enter fullscreen mode Exit fullscreen mode
sort and slices
import "sort"
nums := []int{5, 2, 4, 1, 3}
sort.Ints(nums) // [1 2 3 4 5]
words := []string{"b", "a", "c"}
sort.Strings(words)
// Custom sort.
sort.Slice(nums, func(i, j int) bool {
return nums[i] > nums[j] // Descending.
})
Enter fullscreen mode Exit fullscreen mode
import (
"cmp"
"slices"
)
slices.Sort(nums) // Ascending.
idx, found := slices.BinarySearch(nums, 3) // Requires sorted input.
// cmp.Compare avoids integer overflow in comparison functions.
slices.SortFunc(nums, func(a, b int) int { return cmp.Compare(b, a) }) // Descending.
Enter fullscreen mode Exit fullscreen mode
maps (Go 1.21+)
import (
"maps"
"slices"
)
m := map[string]int{"a": 1, "b": 2}
m2 := maps.Clone(m) // Shallow copy.
maps.Copy(m2, m) // Copy all entries from m into m2.
delete(m, "a") // delete is a built-in function.
keys := slices.Collect(maps.Keys(m)) // Collect keys into a slice.
Enter fullscreen mode Exit fullscreen mode
time
import "time"
now := time.Now()
t := time.Date(2026, 6, 1, 12, 0, 0, 0, time.Local)
// Go uses a reference time (Mon Jan 2 15:04:05 MST 2006) as the layout.
now.Format("2006-01-02 15:04:05")
now.Format(time.RFC3339) // "2026-06-22T12:00:00+09:00"
parsed, _ := time.Parse("2006-01-02", "2026-06-22")
later := now.Add(24 * time.Hour)
diff := later.Sub(now) // time.Duration
fmt.Println(diff.Hours()) // 24.0
time.Sleep(100 * time.Millisecond)
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
Enter fullscreen mode Exit fullscreen mode
os and filepath
import (
"os"
"path/filepath"
)
// File I/O.
data, err := os.ReadFile("file.txt")
err = os.WriteFile("out.txt", data, 0644)
// Directory operations.
entries, _ := os.ReadDir(".")
for _, e := range entries {
fmt.Println(e.Name(), e.IsDir())
}
os.MkdirAll("a/b/c", 0755)
os.Remove("file.txt")
os.RemoveAll("dir/")
// Environment variables.
home := os.Getenv("HOME")
os.Setenv("KEY", "value")
// Path manipulation.
filepath.Join("a", "b", "c") // "a/b/c"
filepath.Base("/a/b/file.txt") // "file.txt"
filepath.Dir("/a/b/file.txt") // "/a/b"
filepath.Ext("file.txt") // ".txt"
abs, _ := filepath.Abs("relative") // Absolute path.
Enter fullscreen mode Exit fullscreen mode
encoding/json
import "encoding/json"
type Point struct {
X int `json:"x"`
Y int `json:"y"`
}
// Encode.
data, err := json.Marshal(Point{X: 1, Y: 2}) // []byte
pretty, err := json.MarshalIndent(Point{X: 1}, "", " ")
// Decode.
var p Point
err = json.Unmarshal(data, &p)
// Streaming.
enc := json.NewEncoder(os.Stdout)
enc.Encode(p)
dec := json.NewDecoder(os.Stdin) // Accepts any io.Reader.
dec.Decode(&p)
// When the type is unknown.
var v any
json.Unmarshal(data, &v)
m := v.(map[string]any)
Enter fullscreen mode Exit fullscreen mode
net/http
import (
"bytes"
"context"
"errors"
"io"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
// Always set a timeout; the default client has none.
client := &http.Client{Timeout: 10 * time.Second}
// GET request.
resp, err := client.Get("https://example.com")
if err != nil {
return err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
// Custom request with context.
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(jsonData))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
resp2, err := client.Do(req)
if err != nil {
return err
}
defer resp2.Body.Close()
// HTTP server with timeouts (required in production).
mux := http.NewServeMux()
mux.HandleFunc("GET /hello/{name}", func(w http.ResponseWriter, r *http.Request) {
name := r.PathValue("name") // Path parameters (Go 1.22+).
fmt.Fprintf(w, "Hello, %s!", name)
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
}
go func() {
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Printf("server error: %v", err)
}
}()
// Graceful shutdown on SIGINT / SIGTERM.
shutdownCtx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
<-shutdownCtx.Done()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Printf("shutdown error: %v", err)
}
Enter fullscreen mode Exit fullscreen mode
log/slog (Go 1.21+)
import "log/slog"
// Default logger writes text to stderr.
slog.Info("server started", "port", 8080)
slog.Error("request failed", "err", err)
// JSON handler for structured logging (e.g., cloud environments).
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
}))
logger.Info("user logged in", "userID", 42, "ip", "127.0.0.1")
// {"time":"...","level":"INFO","msg":"user logged in","userID":42,"ip":"127.0.0.1"}
// Add fields shared across multiple log calls.
logger.With("requestID", "abc123").Info("processing")
Enter fullscreen mode Exit fullscreen mode
Tips and common mistakes
Goroutine leak
// Bad: the goroutine blocks forever if no one receives from ch.
go func() {
ch <- result
}()
// Good: always provide a cancellation path.
go func() {
select {
case ch <- result:
case <-ctx.Done():
return
}
}()
Enter fullscreen mode Exit fullscreen mode
nil interface trap
// Bad: even when *MyError is nil, the interface value is non-nil.
func getError() error {
var err *MyError = nil
return err // The returned error interface is non-nil!
}
if getError() != nil { // true — this branch is taken unexpectedly.
fmt.Println("this prints unexpectedly")
}
// Good: return nil directly.
func getError() error {
return nil
}
Enter fullscreen mode Exit fullscreen mode
Pre-allocate slice capacity
// Bad: append may reallocate the backing array many times.
result := []int{}
for i := 0; i < 10000; i++ {
result = append(result, i)
}
// Good: allocate the full capacity upfront.
result := make([]int, 0, 10000)
for i := 0; i < 10000; i++ {
result = append(result, i)
}
Enter fullscreen mode Exit fullscreen mode
for-loop variable capture (fixed in Go 1.22)
// Go 1.21 and earlier: all goroutines share the same i variable.
for i := 0; i < 5; i++ {
go func() { fmt.Println(i) }() // May print 5 five times.
}
// Go 1.22+: each iteration creates a new i variable.
// The above code now correctly prints 0, 1, 2, 3, 4 in some order.
// Workaround for Go 1.21 and earlier.
for i := 0; i < 5; i++ {
i := i // Shadow i with a new variable for this iteration.
go func() { fmt.Println(i) }()
}
Enter fullscreen mode Exit fullscreen mode
Modules and packages
# Initialize a module.
go mod init github.com/yourname/yourproject
# Go 1.26
go get [email protected]
go get github.com/some/[email protected] # Add a dependency.
go mod tidy # Remove unused dependencies.
go mod vendor # Vendor dependencies.
go build ./... # Build.
go test ./... # Test.
go test -race ./... # Detect data races.
go vet ./... # Static analysis.
go fmt ./... # Format.
Enter fullscreen mode Exit fullscreen mode
답글 남기기