惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
C
Cybersecurity and Infrastructure Security Agency CISA
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
S
Security Affairs
Hacker News: Ask HN
Hacker News: Ask HN
L
Lohrmann on Cybersecurity
PCI Perspectives
PCI Perspectives
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cyber Attacks, Cyber Crime and Cyber Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
MyScale Blog
MyScale Blog
月光博客
月光博客
W
WeLiveSecurity
T
Threat Research - Cisco Blogs
Martin Fowler
Martin Fowler
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Recorded Future
Recorded Future
The GitHub Blog
The GitHub Blog
Webroot Blog
Webroot Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
TaoSecurity Blog
TaoSecurity Blog
P
Proofpoint News Feed
Google DeepMind News
Google DeepMind News
F
Full Disclosure
U
Unit 42
Jina AI
Jina AI
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
L
LINUX DO - 最新话题
宝玉的分享
宝玉的分享
大猫的无限游戏
大猫的无限游戏
The Hacker News
The Hacker News
The Last Watchdog
The Last Watchdog
T
Troy Hunt's Blog
腾讯CDC
T
Threatpost
H
Hacker News: Front Page
P
Palo Alto Networks Blog
博客园 - 聂微东
Last Week in AI
Last Week in AI
有赞技术团队
有赞技术团队
Help Net Security
Help Net Security
L
LINUX DO - 热门话题
N
News and Events Feed by Topic
人人都是产品经理
人人都是产品经理
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Spread Privacy
Spread Privacy

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Go 1.26 Cheat Sheet
lappy · 2026-06-23 · via DEV Community

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)
}

  • Every Go source file starts with a package declaration.
  • An executable program uses package main and a main function.
  • 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
)

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 numbers 0 (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 = '🐹'

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
)

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.


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")
}

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.
}

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)
    }
}

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
}

Note
Deferred call arguments are evaluated when defer is 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.
}

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.

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

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.
}


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.

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.

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))

Caution
A subslice shares its backing array with the original. Mutating one can mutate the other. Use copy when 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)
}

Caution
The zero value of a map is nil. Reading from a nil map is safe, but writing to one panics. Initialize it with make or 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.

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

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)


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.)

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())

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)
}

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)


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()

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
}

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)

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
}


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

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()

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.

// 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
}

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)

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.
}

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())

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
}


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.

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.


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]

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
}

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"


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))


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)
    }
}

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))
}

go test ./...          # Run all tests.
go test -race ./...    # Detect data races.
go test -cover ./...   # Show coverage.


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

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"

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"

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.
})

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.

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.

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()

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.

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)

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)
}

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")


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
    }
}()

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
}

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)
}

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) }()
}


Modules and packages

# Initialize a module.
go mod init github.com/yourname/yourproject

# Go 1.26
go get go@1.26

go get github.com/some/package@v1.2.3   # 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.


Further reading