Skip to content

Go language syntax, tooling, modules, concurrency, testing, and standard library reference.


Terminal window
# Download from https://golang.org/dl/
go version # Check installed Go version
go env # Show all Go environment variables
go env GOPATH # Show GOPATH directory
go env GOROOT # Show Go installation directory
Terminal window
go mod init <module_name> # Initialize a new module (e.g. go mod init myapp)
go mod tidy # Add missing and remove unused dependencies
go mod download # Download all dependencies to local cache
go mod vendor # Copy dependencies into ./vendor directory

Terminal window
go run main.go # Compile and run a Go file directly
go run . # Run the package in current directory
go build # Compile binary into current directory
go build -o myapp # Compile with custom output name
go build ./... # Build all packages in module
go install # Compile and install to $GOPATH/bin
go clean # Remove compiled object files

package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
// Variable declaration
var name string = "Go" // Explicit type declaration
var age int // Zero value: 0
message := "Hello" // Short declaration (inside functions only)
// Multiple variables
var x, y int = 10, 20
a, b := true, 3.14
// Constants
const Pi = 3.14159
const (
StatusOK = 200
StatusError = 500
)
// Basic types
bool // true / false
string // "text" (UTF-8)
int int8 int16 int32 int64 // Signed integers
uint uint8 uint16 uint32 uint64 // Unsigned integers
float32 float64 // Floating point
complex64 complex128 // Complex numbers
byte // Alias for uint8
rune // Alias for int32 (Unicode code point)
// Type conversion (explicit only — no implicit casting)
i := 42
f := float64(i)
u := uint(f)
s := string(rune(65)) // "A"

if x > 10 {
fmt.Println("big")
} else if x > 5 {
fmt.Println("medium")
} else {
fmt.Println("small")
}
// If with init statement
if val, err := strconv.Atoi("42"); err == nil {
fmt.Println(val)
}
switch day {
case "Mon", "Tue", "Wed", "Thu", "Fri":
fmt.Println("Weekday")
case "Sat", "Sun":
fmt.Println("Weekend")
default:
fmt.Println("Unknown")
}
// Typeless switch (replaces long if-else)
switch {
case age < 18:
fmt.Println("Minor")
case age >= 18:
fmt.Println("Adult")
}
// For (Go's only loop keyword)
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// While-style
for x < 100 {
x *= 2
}
// Infinite loop
for {
// break to exit
}
// Range over slice/array
for i, v := range nums {
fmt.Println(i, v)
}
// Range over map
for k, v := range myMap {
fmt.Println(k, v)
}
// Range over string (yields runes)
for i, ch := range "hello" {
fmt.Printf("%d: %c\n", i, ch)
}

func add(a, b int) int {
return a + b
}
// Multiple return values
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// Named return values
func minMax(nums []int) (min, max int) {
min, max = nums[0], nums[0]
for _, v := range nums {
if v < min { min = v }
if v > max { max = v }
}
return // Naked return
}
// Variadic function
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
sum(1, 2, 3) // Pass individual args
sum(nums...) // Spread a slice
// Function as variable
double := func(n int) int { return n * 2 }
fmt.Println(double(5)) // 10
// Function as argument
apply := func(f func(int) int, x int) int { return f(x) }
// Immediately invoked function
result := func(x int) int { return x * x }(9)
// Closure
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
c := counter()
c() // 1
c() // 2
// Defer: runs when surrounding function returns (LIFO order)
defer fmt.Println("cleanup")
// Panic: stops normal execution
panic("something went wrong")
// Recover: catches a panic (must be inside defer)
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()

var arr [3]int // [0, 0, 0]
arr := [3]int{1, 2, 3}
arr := [...]int{4, 5, 6} // Compiler infers length
arr[0] = 10
len(arr) // 3
s := []int{1, 2, 3} // Slice literal
s := make([]int, 5) // Length 5, capacity 5
s := make([]int, 3, 10) // Length 3, capacity 10
s = append(s, 4) // Append single element
s = append(s, 5, 6, 7) // Append multiple
s = append(s, other...) // Append another slice
s[1:3] // Slice of index 1 to 2 (not 3)
s[:3] // From start to index 2
s[2:] // From index 2 to end
copy(dst, src) // Copy elements from src to dst
len(s) // Number of elements
cap(s) // Underlying array capacity
m := map[string]int{"a": 1, "b": 2}
m := make(map[string]int)
m["key"] = 42 // Set value
v := m["key"] // Get value (zero if missing)
v, ok := m["key"] // ok is false if key absent
delete(m, "key") // Remove key
// Iterate
for k, v := range m {
fmt.Println(k, v)
}

type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30} // Named fields
p := Person{"Alice", 30} // Positional (fragile, avoid)
p.Name = "Bob" // Field access
pp := &Person{Name: "Carol"} // Pointer to struct
// Anonymous struct
config := struct {
Host string
Port int
}{"localhost", 8080}
// Value receiver (copy)
func (p Person) Greet() string {
return "Hi, I'm " + p.Name
}
// Pointer receiver (modifies original)
func (p *Person) Birthday() {
p.Age++
}
p.Birthday() // Auto-dereferenced
(&p).Birthday() // Equivalent
type Animal struct {
Name string
}
func (a Animal) Speak() string { return a.Name + " speaks" }
type Dog struct {
Animal // Embedded — promotes fields/methods
Breed string
}
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Lab"}
d.Speak() // Promoted from Animal
d.Name // Promoted field

type Shape interface {
Area() float64
Perimeter() float64
}
type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }
func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) }
// Implicit satisfaction — no "implements" keyword
var s Shape = Rectangle{5, 3}
s.Area() // 15
var i interface{} = "hello" // any type (use `any` in Go 1.18+)
var a any = 42
// Type assertion
s := i.(string) // Panics if wrong type
s, ok := i.(string) // Safe form
// Type switch
switch v := i.(type) {
case int: fmt.Println("int:", v)
case string: fmt.Println("string:", v)
default: fmt.Println("other")
}

x := 42
p := &x // & = address of x
*p = 100 // * = dereference (modifies x)
fmt.Println(x) // 100
// new() allocates zeroed memory, returns pointer
p := new(int)
*p = 7
// Pointers with structs
type Node struct{ Val int }
n := &Node{Val: 1}
n.Val = 2 // Auto-dereferenced (no n->Val like C)

// Idiomatic: return error as last value
func readFile(path string) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("readFile: %w", err) // Wrap error
}
return data, nil
}
// Check and handle
data, err := readFile("config.json")
if err != nil {
log.Fatal(err)
}
// Custom error types
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
// Unwrap errors (Go 1.13+)
errors.Is(err, os.ErrNotExist) // Check error chain
errors.As(err, &myErr) // Extract concrete type from chain

go func() {
fmt.Println("runs concurrently")
}()
go doWork(arg) // Launch any function as goroutine
// Wait for goroutines with WaitGroup
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Println("worker", id)
}(i)
}
wg.Wait() // Block until all Done()
ch := make(chan int) // Unbuffered channel
ch := make(chan int, 10) // Buffered channel (capacity 10)
ch <- 42 // Send (blocks if unbuffered/full)
v := <-ch // Receive (blocks until data)
v, ok := <-ch // ok is false if channel closed
close(ch) // Signal no more sends
// Range over channel
for v := range ch { // Exits when channel closed
fmt.Println(v)
}
// Directional channels (for function params)
func producer(out chan<- int) { out <- 1 } // Send-only
func consumer(in <-chan int) { fmt.Println(<-in) } // Receive-only
select {
case msg := <-ch1:
fmt.Println("ch1:", msg)
case msg := <-ch2:
fmt.Println("ch2:", msg)
case ch3 <- "hello":
fmt.Println("sent to ch3")
default:
fmt.Println("no channel ready") // Non-blocking
}
var mu sync.Mutex
mu.Lock()
// critical section
mu.Unlock()
// RWMutex: multiple readers, one writer
var rw sync.RWMutex
rw.RLock() / rw.RUnlock() // Shared read lock
rw.Lock() / rw.Unlock() // Exclusive write lock
// Once: run exactly once
var once sync.Once
once.Do(func() { fmt.Println("only once") })
// Atomic operations
var counter int64
atomic.AddInt64(&counter, 1)
atomic.LoadInt64(&counter)

// Generic function
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
}
// Type constraints
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 struct
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 {
n := len(s.items) - 1
v := s.items[n]
s.items = s.items[:n]
return v
}

Terminal window
go get github.com/pkg/errors # Add a dependency
go get github.com/pkg/errors@v1.9.1 # Specific version
go get github.com/pkg/errors@latest # Latest version
go list -m all # List all module dependencies
go mod graph # Show dependency graph
go mod why github.com/pkg/errors # Explain why dep is needed
// Import styles
import "fmt" // Standard library
import "github.com/pkg/errors" // Third-party
import ( // Grouped imports
"fmt"
"os"
"github.com/pkg/errors"
)
import _ "image/png" // Blank import (side effects only)
import f "fmt" // Aliased import

// Read entire file
data, err := os.ReadFile("file.txt")
// Write entire file
err := os.WriteFile("file.txt", []byte("hello"), 0644)
// Open / close
f, err := os.Open("file.txt") // Read-only
defer f.Close()
f, err := os.Create("file.txt") // Write (truncates)
f, err := os.OpenFile("file.txt", os.O_APPEND|os.O_WRONLY, 0644)
// Buffered reader
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fmt.Println(scanner.Text()) // Line by line
}
// Buffered writer
w := bufio.NewWriter(f)
fmt.Fprintln(w, "line")
w.Flush() // Don't forget to flush!

Terminal window
go test ./... # Run all tests in module
go test -v ./... # Verbose output
go test -run TestFuncName # Run specific test
go test -bench=. # Run benchmarks
go test -cover # Show coverage summary
go test -coverprofile=cov.out # Write coverage profile
go tool cover -html=cov.out # Open coverage in browser
go test -race ./... # Run with race detector
// Basic test (file must end in _test.go)
func TestAdd(t *testing.T) {
got := add(2, 3)
if got != 5 {
t.Errorf("add(2,3) = %d; want 5", got)
}
}
// Table-driven tests
func TestMultiply(t *testing.T) {
tests := []struct {
a, b, want int
}{
{2, 3, 6},
{0, 5, 0},
{-1, 4, -4},
}
for _, tc := range tests {
got := multiply(tc.a, tc.b)
if got != tc.want {
t.Errorf("multiply(%d,%d) = %d; want %d", tc.a, tc.b, got, tc.want)
}
}
}
// Benchmark
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
add(1, 2)
}
}
// Subtests
func TestGroup(t *testing.T) {
t.Run("case A", func(t *testing.T) { /* ... */ })
t.Run("case B", func(t *testing.T) { /* ... */ })
}

fmt.Println("hello", x) # Print with newline
fmt.Printf("name: %s, age: %d\n", n, a) # Formatted print
fmt.Sprintf("val: %v", x) # Format to string
fmt.Fprintf(w, "to writer: %s", s) # Format to io.Writer
fmt.Errorf("wrap: %w", err) # Create formatted error
// Verbs: %v (default), %T (type), %d (int), %f (float), %s (string), %q (quoted), %p (pointer)
strings.Contains(s, "sub") # true/false
strings.HasPrefix(s, "pre") # true/false
strings.HasSuffix(s, "suf") # true/false
strings.ToUpper(s) / strings.ToLower(s)
strings.TrimSpace(s) # Remove leading/trailing whitespace
strings.Trim(s, "!?") # Remove specific chars from both ends
strings.Split(s, ",") # []string
strings.Join(parts, "-") # string
strings.Replace(s, "old", "new", n) # n=-1 replaces all
strings.Count(s, "sub") # Occurrences
strings.Index(s, "sub") # First index, -1 if not found
strings.TrimPrefix(s, "prefix")
strings.Fields(s) # Split on any whitespace
strconv.Atoi("42") # stringint (returns int, error)
strconv.Itoa(42) # intstring
strconv.ParseFloat("3.14", 64) # stringfloat64
strconv.FormatFloat(3.14, 'f', 2, 64) # float64string "3.14"
strconv.ParseBool("true") # stringbool
strconv.FormatBool(true) # boolstring
os.Getenv("HOME") # Get environment variable
os.Setenv("KEY", "val") # Set environment variable
os.Args # []string of CLI arguments
os.Exit(1) # Exit with code
os.Mkdir("dir", 0755) # Create directory
os.MkdirAll("a/b/c", 0755) # Create nested directories
os.Remove("file.txt") # Delete file
os.Rename("old", "new") # Move/rename
os.Stat("file.txt") # Get file info (FileInfo, error)
filepath.Join("a", "b", "c") # "a/b/c" (OS-safe)
filepath.Dir("/a/b/c") # "/a/b"
filepath.Base("/a/b/c") # "c"
filepath.Ext("file.txt") # ".txt"
time.Now() # Current time
time.Now().Unix() # Unix timestamp (seconds)
time.Sleep(2 * time.Second) # Sleep for duration
time.Second / time.Millisecond / time.Minute
t := time.Now()
t.Format("2006-01-02 15:04:05") // Format (Go's reference time)
time.Parse("2006-01-02", "2024-01-15") // Parse string to Time
since := time.Since(start) // Duration elapsed
until := time.Until(deadline) // Duration remaining
ticker := time.NewTicker(1 * time.Second)
timer := time.NewTimer(5 * time.Second)

// Create contexts
ctx := context.Background() // Root context (never cancelled)
ctx := context.TODO() // Placeholder context
// With cancellation
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Always call cancel
// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// With deadline
deadline := time.Now().Add(10 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
// With value
ctx = context.WithValue(ctx, "userID", 42)
val := ctx.Value("userID").(int)
// Check cancellation in goroutines
select {
case <-ctx.Done():
fmt.Println("cancelled:", ctx.Err()) // context.Canceled or DeadlineExceeded
default:
// continue work
}

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
})
http.ListenAndServe(":8080", nil)
// With custom mux
mux := http.NewServeMux()
mux.HandleFunc("/api/users", usersHandler)
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server.ListenAndServe()
// Simple GET
resp, err := http.Get("https://api.example.com/data")
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
// With timeout
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get("https://api.example.com")
// POST with JSON
payload, _ := json.Marshal(map[string]string{"key": "val"})
resp, err := http.Post(url, "application/json", bytes.NewBuffer(payload))
// Custom request with headers
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, err := client.Do(req)

type User struct {
Name string `json:"name"`
Email string `json:"email,omitempty"` // Omit if empty
pass string // Unexported = ignored
}
// Struct → JSON
data, err := json.Marshal(u)
jsonStr := string(data)
// JSON → Struct
var u User
err := json.Unmarshal([]byte(jsonStr), &u)
// Stream encoding/decoding
enc := json.NewEncoder(w) // w = io.Writer (e.g. ResponseWriter)
enc.Encode(u)
dec := json.NewDecoder(r.Body) // r.Body = io.Reader
dec.Decode(&u)
// Arbitrary JSON
var raw map[string]interface{}
json.Unmarshal(data, &raw)

Terminal window
go fmt ./... # Format all Go source files
go vet ./... # Report likely mistakes in code
go doc fmt.Println # Show documentation for a symbol
go doc -all fmt # Show all docs for a package
godoc -http=:6060 # Serve local documentation
go generate ./... # Run //go:generate directives
gopls # Official Go language server (LSP)
staticcheck ./... # Advanced static analysis (install separately)
dlv debug main.go # Debug with Delve debugger

Don't communicate by sharing memory; share memory by communicating.
Concurrency is not parallelism.
Errors are values — handle them, don't ignore them.
The bigger the interface, the weaker the abstraction.
Accept interfaces, return structs.
Make the zero value useful.
A little copying is better than a little dependency.
Clear is better than clever.