Go Cheatsheet

Complete development reference

Development
Contents
๐Ÿ“ฆ

Basics & Variables

// Hello World
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

// Variable declarations
var name string = "Bharath"       // explicit type
var age = 25                       // type inferred
city := "NYC"                      // short declaration (inside func only)

// Multiple declarations
var (
    x int    = 10
    y float64 = 3.14
    z bool    = true
)

// Constants
const Pi = 3.14159
const (
    StatusOK    = 200
    StatusNotFound = 404
)

// iota โ€” auto-incrementing constants
const (
    Sunday int = iota   // 0
    Monday                // 1
    Tuesday               // 2
)

// Zero values
// int โ†’ 0, float โ†’ 0.0, bool โ†’ false, string โ†’ "", pointer โ†’ nil

// Type conversions (no implicit casting)
i := 42
f := float64(i)
s := string(rune(i))
๐Ÿงฑ

Types & Structs

// Basic types
// bool, string
// int, int8, int16, int32, int64
// uint, uint8(byte), uint16, uint32, uint64
// float32, float64
// complex64, complex128
// rune (alias for int32, represents a Unicode code point)

// Struct
type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email,omitempty"`
    CreatedAt time.Time `json:"created_at"`
}

// Create instances
u1 := User{Name: "Bob", Email: "bob@mail.com"}
u2 := User{"1", "Bob", "bob@mail.com", time.Now()}  // positional
u3 := new(User)    // returns *User (pointer), zero-valued

// Methods (value receiver)
func (u User) FullName() string {
    return u.Name
}

// Methods (pointer receiver โ€” can mutate)
func (u *User) SetEmail(email string) {
    u.Email = email
}

// Embedding (composition over inheritance)
type Admin struct {
    User                  // embedded โ€” Admin "inherits" User's fields/methods
    Role string
}
admin := Admin{User: User{Name: "Alice"}, Role: "super"}
fmt.Println(admin.Name) // promoted field access

// Type aliases & custom types
type UserID int64
type Celsius float64
type StringSlice []string
โš™๏ธ

Functions

// Basic function
func add(a, b int) int {
    return a + b
}

// Multiple return values
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("division by zero")
    }
    return a / b, nil
}

// Named return values
func swap(a, b int) (x, y int) {
    x, y = b, a
    return // naked return
}

// Variadic functions
func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

// Function as value / first-class
var op func(int, int) int = add

// Anonymous / closure
double := func(n int) int { return n * 2 }

// Higher-order function
func apply(nums []int, fn func(int) int) []int {
    result := make([]int, len(nums))
    for i, n := range nums {
        result[i] = fn(n)
    }
    return result
}

// Defer โ€” runs when function returns (LIFO)
func readFile(path string) {
    f, _ := os.Open(path)
    defer f.Close()  // guaranteed cleanup
    // ... read file
}

// Init function โ€” runs before main()
func init() {
    // package initialization
}
๐Ÿ“

Interfaces

// Interface definition
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// Interface composition
type ReadWriter interface {
    Reader
    Writer
}

// Implicit implementation โ€” no "implements" keyword
type MyFile struct{ data []byte }

func (f *MyFile) Read(p []byte) (int, error) {
    copy(p, f.data)
    return len(f.data), nil
}
// *MyFile now satisfies Reader interface

// Empty interface โ€” accepts any type
var anything interface{} = "hello"
var anything2 any = 42   // 'any' is alias for interface{} (Go 1.18+)

// Type assertion
val, ok := anything.(string)
if ok {
    fmt.Println(val)
}

// Type switch
func describe(i interface{}) string {
    switch v := i.(type) {
    case int:
        return fmt.Sprintf("int: %d", v)
    case string:
        return fmt.Sprintf("string: %s", v)
    default:
        return "unknown"
    }
}

// Stringer interface (like toString)
func (u User) String() string {
    return fmt.Sprintf("%s (%s)", u.Name, u.Email)
}
๐Ÿ’ก Key Principle Go uses structural (duck) typing: if a type has the right methods, it implements the interface automatically. Accept interfaces, return structs.
๐Ÿ“š

Arrays, Slices & Maps

// Array (fixed size)
var arr [5]int = [5]int{1, 2, 3, 4, 5}

// Slice (dynamic, backed by array)
s := []int{1, 2, 3}
s = append(s, 4, 5)       // append elements
s = append(s, other...)    // append another slice

// Make with length & capacity
s2 := make([]int, 0, 100) // len=0, cap=100

// Slicing
sub := s[1:3]             // [2, 3] โ€” shares underlying array!
cpy := make([]int, len(s))
copy(cpy, s)               // true copy

// Delete element at index i (Go 1.21+: slices.Delete)
s = append(s[:i], s[i+1:]...)

// Maps
m := map[string]int{
    "one": 1,
    "two": 2,
}
m["three"] = 3             // set
val, ok := m["one"]        // get with existence check
delete(m, "two")           // delete key

// Iterate
for key, val := range m {
    fmt.Println(key, val)
}

// Iterate slice
for i, v := range s {
    fmt.Println(i, v)
}

// Useful packages (Go 1.21+)
import "slices"
slices.Sort(s)
slices.Contains(s, 3)
idx := slices.Index(s, 3)

import "maps"
keys := maps.Keys(m)
maps.Clone(m)
๐Ÿ”„

Control Flow

// If (no parentheses, braces required)
if x > 0 {
    fmt.Println("positive")
} else if x == 0 {
    fmt.Println("zero")
} else {
    fmt.Println("negative")
}

// If with init statement
if err := doSomething(); err != nil {
    log.Fatal(err)
}

// For loop (Go's only loop)
for i := 0; i < 10; i++ { }

// While-style
for condition { }

// Infinite loop
for { break }

// Range loop
for i, v := range slice { }
for k, v := range myMap { }
for i, ch := range "hello" { } // iterates runes

// Switch (no break needed โ€” implicit)
switch day {
case "Mon", "Tue", "Wed", "Thu", "Fri":
    fmt.Println("weekday")
case "Sat", "Sun":
    fmt.Println("weekend")
default:
    fmt.Println("invalid")
}

// Switch without condition (cleaner if/else chain)
switch {
case t.Hour() < 12:
    fmt.Println("morning")
case t.Hour() < 17:
    fmt.Println("afternoon")
default:
    fmt.Println("evening")
}

// Select (switch for channels)
select {
case msg := <-ch1:
    fmt.Println(msg)
case ch2 <- data:
    fmt.Println("sent")
case <-time.After(5 * time.Second):
    fmt.Println("timeout")
}
๐Ÿš€

Goroutines & Channels

// Start a goroutine
go doWork()

// Anonymous goroutine
go func(msg string) {
    fmt.Println(msg)
}("hello")

// WaitGroup โ€” wait for goroutines to finish
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()

// Unbuffered channel (synchronous)
ch := make(chan int)
go func() { ch <- 42 }()
val := <-ch

// Buffered channel
ch := make(chan string, 10)  // buffer size 10
ch <- "msg"                    // won't block until buffer full

// Directional channels
func producer(out chan<- int) { out <- 1 }   // send-only
func consumer(in <-chan int)  { <-in }         // receive-only

// Range over channel
go func() {
    for i := 0; i < 5; i++ { ch <- i }
    close(ch)  // must close for range to exit
}()
for val := range ch {
    fmt.Println(val)
}
๐Ÿ”€

Concurrency Patterns

Mutex

type SafeCounter struct {
    mu    sync.Mutex
    count int
}

func (c *SafeCounter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

// RWMutex โ€” multiple readers, single writer
var rw sync.RWMutex
rw.RLock()   // read lock
rw.RUnlock()

Context (Cancellation & Timeouts)

// Timeout context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// Cancel context
ctx, cancel := context.WithCancel(context.Background())

// Use in goroutine
go func(ctx context.Context) {
    select {
    case <-ctx.Done():
        fmt.Println("cancelled:", ctx.Err())
        return
    case result := <-doWork(ctx):
        fmt.Println(result)
    }
}(ctx)

Worker Pool

func workerPool(jobs <-chan int, results chan<- int, workers int) {
    var wg sync.WaitGroup
    for w := 0; w < workers; w++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for job := range jobs {
                results <- job * 2
            }
        }()
    }
    wg.Wait()
    close(results)
}

errgroup

import "golang.org/x/sync/errgroup"

g, ctx := errgroup.WithContext(context.Background())
g.Go(func() error { return fetchA(ctx) })
g.Go(func() error { return fetchB(ctx) })
if err := g.Wait(); err != nil {
    log.Fatal(err) // first error cancels all
}
๐Ÿšจ

Error Handling

// Basic error check (the Go way)
result, err := doSomething()
if err != nil {
    return fmt.Errorf("failed to do something: %w", err) // wrap error
}

// Custom error type
type NotFoundError struct {
    ID   string
    Kind string
}

func (e *NotFoundError) Error() string {
    return fmt.Sprintf("%s %s not found", e.Kind, e.ID)
}

// Sentinel errors
var ErrNotFound = errors.New("not found")
var ErrUnauthorized = errors.New("unauthorized")

// errors.Is โ€” check error chain
if errors.Is(err, ErrNotFound) {
    // handle not found
}

// errors.As โ€” extract typed error
var nfErr *NotFoundError
if errors.As(err, &nfErr) {
    fmt.Println(nfErr.Kind, nfErr.ID)
}

// Panic & Recover (use sparingly โ€” not for normal errors)
func safeDiv(a, b int) (int, error) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recovered:", r)
        }
    }()
    return a / b, nil
}
๐Ÿ“‚

I/O & Files

// Read entire file
data, err := os.ReadFile("file.txt")

// Write file
err := os.WriteFile("out.txt", []byte("hello"), 0644)

// Buffered reading
f, _ := os.Open("file.txt")
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
    fmt.Println(scanner.Text())
}

// JSON encode/decode
import "encoding/json"

// Struct โ†’ JSON
jsonBytes, _ := json.Marshal(user)
jsonStr := string(jsonBytes)

// JSON โ†’ Struct
var u User
json.Unmarshal([]byte(jsonStr), &u)

// Pretty print
jsonBytes, _ := json.MarshalIndent(user, "", "  ")

// Streaming JSON
encoder := json.NewEncoder(os.Stdout)
encoder.Encode(user)

decoder := json.NewDecoder(resp.Body)
decoder.Decode(&u)
๐ŸŒ

HTTP & APIs

import "net/http"

// Simple HTTP server
func main() {
    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, %s!", r.URL.Query().Get("name"))
    })
    http.ListenAndServe(":8080", nil)
}

// JSON API handler
func getUser(w http.ResponseWriter, r *http.Request) {
    user := User{Name: "Bob", Email: "bob@mail.com"}
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}

// HTTP client
resp, err := http.Get("https://api.example.com/data")
if err != nil { log.Fatal(err) }
defer resp.Body.Close()

var data Response
json.NewDecoder(resp.Body).Decode(&data)

// HTTP client with timeout
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get("https://api.example.com")

// Middleware pattern
func logging(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}
๐Ÿงช

Testing

// file: math_test.go
package math

import "testing"

// Basic test
func TestAdd(t *testing.T) {
    got := Add(2, 3)
    want := 5
    if got != want {
        t.Errorf("Add(2, 3) = %d, want %d", got, want)
    }
}

// Table-driven tests (idiomatic Go)
func TestDivide(t *testing.T) {
    tests := []struct {
        name    string
        a, b    float64
        want    float64
        wantErr bool
    }{
        {"ok", 10, 2, 5, false},
        {"div by zero", 10, 0, 0, true},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := 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
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(2, 3)
    }
}

// Run: go test ./...
// Run: go test -v -run TestAdd
// Run: go test -bench=.
๐Ÿงฌ

Generics (Go 1.18+)

// Generic function
func Map[T, U any](s []T, fn func(T) U) []U {
    result := make([]U, len(s))
    for i, v := range s {
        result[i] = fn(v)
    }
    return result
}

// Constrained generic
type Number interface {
    ~int | ~int64 | ~float64
}

func Sum[T Number](nums []T) T {
    var total T
    for _, n := range nums {
        total += n
    }
    return total
}

// comparable constraint
func Contains[T comparable](s []T, target T) bool {
    for _, v := range s {
        if v == target { return true }
    }
    return false
}

// Generic struct
type Stack[T any] struct {
    items []T
}
func (s *Stack[T]) Push(item T) { s.items = append(s.items, item) }
func (s *Stack[T]) Pop() (T, bool) {
    if len(s.items) == 0 {
        var zero T
        return zero, false
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item, true
}
๐Ÿ“ฆ

Modules & Packages

# Initialize module
go mod init github.com/user/myproject

# Add dependency
go get github.com/gin-gonic/gin@latest

# Tidy โ€” remove unused, add missing
go mod tidy

# Vendor dependencies
go mod vendor

# Common project structure
myproject/
โ”œโ”€โ”€ cmd/
โ”‚   โ””โ”€โ”€ server/
โ”‚       โ””โ”€โ”€ main.go          # entry point
โ”œโ”€โ”€ internal/                 # private packages
โ”‚   โ”œโ”€โ”€ handler/
โ”‚   โ”œโ”€โ”€ service/
โ”‚   โ””โ”€โ”€ repository/
โ”œโ”€โ”€ pkg/                      # public packages
โ”‚   โ””โ”€โ”€ utils/
โ”œโ”€โ”€ go.mod
โ”œโ”€โ”€ go.sum
โ””โ”€โ”€ Makefile
๐Ÿ“ Naming Conventions
  • Exported: starts with uppercase โ€” User, GetName()
  • Unexported: starts with lowercase โ€” user, getName()
  • Packages: short, lowercase, no underscores โ€” http, json
  • Interfaces: single-method โ†’ suffix with -er โ€” Reader, Writer
๐ŸŽฏ

Common Patterns

Functional Options

type Option func(*Server)

func WithPort(port int) Option {
    return func(s *Server) { s.port = port }
}

func WithTimeout(d time.Duration) Option {
    return func(s *Server) { s.timeout = d }
}

func NewServer(opts ...Option) *Server {
    s := &Server{port: 8080, timeout: 30 * time.Second}
    for _, opt := range opts { opt(s) }
    return s
}

srv := NewServer(WithPort(3000), WithTimeout(10*time.Second))

Repository Pattern

type UserRepository interface {
    FindByID(ctx context.Context, id string) (*User, error)
    Create(ctx context.Context, user *User) error
    Update(ctx context.Context, user *User) error
    Delete(ctx context.Context, id string) error
}

type pgUserRepo struct { db *sql.DB }

func (r *pgUserRepo) FindByID(ctx context.Context, id string) (*User, error) {
    var u User
    err := r.db.QueryRowContext(ctx, "SELECT * FROM users WHERE id=$1", id).Scan(&u.ID, &u.Name)
    return &u, err
}
๐Ÿ’ก Go Proverbs
  • Don't communicate by sharing memory; share memory by communicating
  • A little copying is better than a little dependency
  • Make the zero value useful
  • Accept interfaces, return structs
  • Errors are values โ€” handle them