Go (Golang) Cheatsheet
Complete reference guide for Go programming language - Syntax, Concurrency, Standard Library, and Best Practices
Basics & Syntax
Hello World
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Basic Go program structure. Every executable Go program must have a main package and main function.
Package Declaration
package main
// Import multiple packages
import (
"fmt"
"math"
"strings"
)
Package declaration and imports. Use grouped import syntax for cleaner code.
Variable Declaration
var name string = "Go"
// Type inference
var version = "1.21"
// Short declaration (inside functions)
count := 10
// Multiple variables
var x, y int = 1, 2
a, b := "hello", "world"
Different ways to declare variables in Go. Short declaration (:=) can only be used inside functions.
Constants
const Pi = 3.14159
// Typed constant
const MaxSize int = 1024
// Multiple constants
const (
StatusOK = 200
StatusNotFound = 404
)
// Iota for enumerated constants
const (
Red = iota // 0
Green // 1
Blue // 2
)
Constants are immutable values. Iota creates incrementing constants starting from 0.
Comments
/*
Multi-line
comment
*/
// Documentation comment (for godoc)
// Package math provides basic constants
// and mathematical functions.
Go supports single-line and multi-line comments. Comments starting with // are used by godoc for documentation.
Zero Values
var f float64 // 0.0
var b bool // false
var s string // ""
var p *int // nil
var sl []int // nil
var m map[string]int // nil
Default values for uninitialized variables in Go. This prevents undefined behavior.
Data Types
Basic Types
var i int // Platform-dependent (32 or 64 bits)
var i8 int8 // -128 to 127
var i16 int16 // -32768 to 32767
var i32 int32 // -2147483648 to 2147483647
var i64 int64 // Large range
// Unsigned integers
var u uint // Platform-dependent
var u8 uint8 // 0 to 255 (byte alias)
var u16 uint16
var u32 uint32
var u64 uint64
// Floating point
var f32 float32
var f64 float64
// Complex numbers
var c64 complex64
var c128 complex128
Go has a rich set of numeric types with specific sizes for precise memory usage.
Strings & Runes
var s string = "Hello, 世界"
// Rune (int32 alias for Unicode code points)
var r rune = 'A'
var r2 rune = '世'
// String operations
len := len(s) // Length in bytes
chars := []rune(s) // Convert to rune slice
sub := s[0:5] // Slicing
concat := s + "!" // Concatenation
contains := strings.Contains(s, "世界")
Strings are immutable byte slices. Runes represent Unicode code points. Use string package for manipulation.
Booleans
var isFalse bool = false
// Logical operations
result1 := true && false // AND: false
result2 := true || false // OR: true
result3 := !true // NOT: false
// Comparison operators
equal := (5 == 5) // true
notEqual := (5 != 3) // true
greater := (10 > 5) // true
less := (3 < 2) // false
Boolean type with logical operators (&&, ||, !) and comparison operators (==, !=, <, >, <=, >=).
Pointers
var p *int = &x // Address of x
fmt.Println(*p) // Dereference: 42
*p = 100 // Modify through pointer
fmt.Println(x) // 100
// new() allocates zeroed memory
ptr := new(int)
*ptr = 10
Pointers hold memory addresses. Use & to get address, * to dereference. Go has no pointer arithmetic.
Type Conversions
var f float64 = float64(i)
var u uint = uint(f)
// String conversions
s := string(65) // "A" (rune to string)
str := fmt.Sprintf("%d", i) // Int to string
// strconv package for parsing
num, err := strconv.Atoi("123")
str2 := strconv.Itoa(456)
Go requires explicit type conversions. Use T(v) syntax. strconv package handles string conversions.
Type Aliases & Definitions
type MyInt = int
var a int = 5
var b MyInt = a // OK, same type
// Type definition (new type)
type MyNewInt int
var c int = 5
// var d MyNewInt = c // Error: type mismatch
var d MyNewInt = MyNewInt(c) // OK with conversion
// Commonly used for readability
type ID int64
type Currency float64
Type aliases create alternative names for existing types. Type definitions create distinct new types.
Control Flow
If Statements
if x > 10 {
fmt.Println("x is greater than 10")
}
// If with initialization
if val := computeValue(); val > 100 {
fmt.Println("Large value:", val)
} else {
fmt.Println("Small value:", val)
}
// If-else chain
if score >= 90 {
grade = "A"
} else if score >= 80 {
grade = "B"
} else {
grade = "C"
}
If statements can include initialization. Variables declared in if are scoped to the if block.
For Loops
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// While-like loop
count := 0
for count < 5 {
fmt.Println(count)
count++
}
// Infinite loop
for {
// Break with condition
if condition {
break
}
}
// Continue statement
for i := 0; i < 10; i++ {
if i%2 == 0 {
continue // Skip even numbers
}
fmt.Println(i)
}
Go has only one loop construct: for. It can be used as traditional for, while, or infinite loop.
Range Loops
nums := []int{2, 3, 4}
for i, num := range nums {
fmt.Printf("index %d: value %d\n", i, num)
}
// Range over map
m := map[string]int{"a": 1, "b": 2}
for k, v := range m {
fmt.Println(k, v)
}
// Range over string (runes)
for i, r := range "Hello" {
fmt.Printf("%d: %c\n", i, r)
}
// Ignore index/value
for _, num := range nums {
// Only use value
}
for i := range nums {
// Only use index
}
Range iterates over elements in slices, arrays, maps, strings, and channels. Returns index/key and value.
Switch Statements
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("macOS")
case "linux":
fmt.Println("Linux")
default:
fmt.Println(os)
}
// Switch without expression (if-else alternative)
switch {
case hour < 12:
fmt.Println("Good morning!")
case hour < 17:
fmt.Println("Good afternoon!")
default:
fmt.Println("Good evening!")
}
// Type switch
var i interface{} = "hello"
switch v := i.(type) {
case int:
fmt.Println("Integer:", v)
case string:
fmt.Println("String:", v)
default:
fmt.Println("Unknown type")
}
Switch in Go is more flexible than in other languages. Cases don't fall through by default (use fallthrough keyword).
Defer Statement
func readFile() {
f, err := os.Open("file.txt")
if err != nil {
return
}
defer f.Close() // Executes when function returns
// Read file contents...
}
// Multiple defers (LIFO order)
func example() {
defer fmt.Println("First defer")
defer fmt.Println("Second defer")
fmt.Println("Function body")
} // Prints: Function body, Second defer, First defer
// Defer with arguments evaluation
func deferExample() {
i := 1
defer fmt.Println(i) // Prints 1, not 2
i = 2
return
}
Defer schedules a function call to run when the surrounding function returns. Useful for cleanup tasks.
Panic & Recover
func mayPanic() {
panic("something went wrong")
}
// Recover captures panic
func safeCall() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
mayPanic()
fmt.Println("This won't be printed")
}
// Built-in panics
var s []int
fmt.Println(s[0]) // Panic: runtime error: index out of range
var m map[string]int
m["key"] = 1 // Panic: assignment to entry in nil map
Panic stops execution and unwinds the stack. Recover can capture panics in deferred functions. Use sparingly.
Functions
Basic Functions
func add(x int, y int) int {
return x + y
}
// Same type parameters can be grouped
func multiply(x, y int) int {
return x * y
}
// Multiple return values
func swap(x, y string) (string, string) {
return y, x
}
// Named return values
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // "naked" return
}
Functions can have multiple parameters and return values. Named returns act as variables defined at top of function.
Variadic Functions
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
// Calling variadic functions
result1 := sum(1, 2, 3) // 6
result2 := sum(1, 2, 3, 4, 5) // 15
// Pass slice to variadic function
nums := []int{1, 2, 3, 4}
result3 := sum(nums...)
// Variadic with other parameters
func join(sep string, strs ...string) string {
return strings.Join(strs, sep)
}
Variadic functions accept zero or more arguments of a specified type. Use ... before type in parameter list.
Function Values
func add(a, b int) int { return a + b }
func multiply(a, b int) int { return a * b }
// Assign function to variable
var operation func(int, int) int
operation = add
result := operation(3, 4) // 7
operation = multiply
result = operation(3, 4) // 12
// Function as parameter
func compute(x, y int, op func(int, int) int) int {
return op(x, y)
}
// Anonymous function
func() {
fmt.Println("Immediately invoked")
}()
Functions are first-class values in Go. They can be assigned to variables, passed as arguments, and returned from functions.
Closures
func counter() func() int {
i := 0
return func() int {
i++
return i
}
}
// Using the closure
next := counter()
fmt.Println(next()) // 1
fmt.Println(next()) // 2
fmt.Println(next()) // 3
// Each closure has its own state
anotherCounter := counter()
fmt.Println(anotherCounter()) // 1
// Practical closure: Adder
func adder(start int) func(int) int {
return func(x int) int {
start += x
return start
}
}
Closures are functions that reference variables from outside their body. They encapsulate state.
Methods
type Rectangle struct {
Width, Height float64
}
// Method with value receiver
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// Method with pointer receiver
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
// Using methods
rect := Rectangle{Width: 10, Height: 5}
area := rect.Area() // 50
rect.Scale(2) // Width=20, Height=10
// Method on non-struct type
type MyInt int
func (m MyInt) Double() MyInt {
return m * 2
}
Methods are functions with a receiver argument. Pointer receivers can modify the value, value receivers cannot.
init() Function
func init() {
fmt.Println("Package initialization")
}
// Multiple init() functions execute in order
func init() {
fmt.Println("Second initialization")
}
func main() {
fmt.Println("Main function")
}
// Output:
// Package initialization
// Second initialization
// Main function
// Common use cases:
var config string
func init() {
// Initialize package variables
config = os.Getenv("APP_CONFIG")
if config == "" {
config = "default"
}
}
init() functions are automatically executed before main(). Each package can have multiple init() functions.
Structs & Interfaces
Structs
type Person struct {
Name string
Age int
}
// Create struct instances
var p1 Person // Zero value
p2 := Person{"Alice", 30} // Literal
p3 := Person{Name: "Bob", Age: 25} // Field names
p4 := &Person{"Charlie", 35} // Pointer
// Access fields
p1.Name = "David"
age := p2.Age
// Anonymous structs
point := struct {
X, Y int
}{10, 20}
// Nested structs
type Address struct {
City, Country string
}
type Employee struct {
Person
Address Address
Salary float64
}
Structs are collections of fields. They can be nested and support anonymous structs for one-time use.
Struct Tags
type User struct {
ID int `json:"id" db:"user_id"`
Username string `json:"username" validate:"required"`
Email string `json:"email,omitempty"`
Password string `json:"-"` // Never marshal
CreatedAt time.Time `json:"created_at"`
}
// Using tags with encoding/json
func example() {
user := User{
ID: 1,
Username: "johndoe",
Email: "",
}
jsonData, err := json.Marshal(user)
// {"id":1,"username":"johndoe"}
// Email omitted due to omitempty
}
// Reading tags with reflection
func printTags() {
t := reflect.TypeOf(User{})
field := t.Field(0)
tag := field.Tag.Get("json")
fmt.Println(tag) // "id"
}
Struct tags are metadata strings attached to struct fields, commonly used by encoding packages and validators.
Interfaces
type Shape interface {
Area() float64
Perimeter() float64
}
// Implement interface implicitly
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)
}
// Using interface
func printArea(s Shape) {
fmt.Println("Area:", s.Area())
}
rect := Rectangle{Width: 10, Height: 5}
printArea(rect) // Area: 50
// Empty interface (any type)
func describe(i interface{}) {
fmt.Printf("Type: %T, Value: %v\n", i, i)
}
Interfaces define method sets. Types implement interfaces implicitly by implementing all methods. Empty interface (interface{}) accepts any value.
Type Assertions
var i interface{} = "hello"
s := i.(string)
fmt.Println(s) // hello
// Type assertion with ok check
s, ok := i.(string)
if ok {
fmt.Println("String:", s)
} else {
fmt.Println("Not a string")
}
// Failed assertion causes panic
// f := i.(float64) // panic!
// Practical example
func processValue(val interface{}) {
switch v := val.(type) {
case int:
fmt.Println("Integer:", v*2)
case string:
fmt.Println("String:", strings.ToUpper(v))
default:
fmt.Println("Unknown type")
}
}
Type assertions extract concrete values from interfaces. Use the "comma ok" idiom to avoid panics with failed assertions.
Embedded Types
type Person struct {
Name string
Age int
}
type Employee struct {
Person // Embedded type
Department string
Salary float64
}
// Using embedded type
emp := Employee{
Person: Person{Name: "Alice", Age: 30},
Department: "Engineering",
Salary: 75000,
}
// Access embedded fields directly
fmt.Println(emp.Name) // Alice (promoted field)
fmt.Println(emp.Person.Age) // 30 (explicit)
// Embedded interfaces
type Reader interface {
Read([]byte) (int, error)
}
type Writer interface {
Write([]byte) (int, error)
}
type ReadWriter interface {
Reader
Writer
}
Embedded types allow composition. Fields and methods of embedded types are promoted to the outer type (but can be shadowed).
Stringer Interface
type Stringer interface {
String() string
}
// Custom type implementing Stringer
type Point struct {
X, Y int
}
func (p Point) String() string {
return fmt.Sprintf("Point(%d, %d)", p.X, p.Y)
}
// Using the Stringer
p := Point{X: 10, Y: 20}
fmt.Println(p) // Prints: Point(10, 20)
// Another example with enum-like type
type Color int
const (
Red Color = iota
Green
Blue
)
func (c Color) String() string {
switch c {
case Red:
return "Red"
case Green:
return "Green"
case Blue:
return "Blue"
default:
return "Unknown"
}
}
The Stringer interface (from fmt package) allows custom string representation for types. fmt.Println uses this automatically.
Concurrency
Goroutines
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world") // New goroutine
say("hello") // Current goroutine
}
// Anonymous function goroutine
go func(msg string) {
fmt.Println(msg)
}("starting")
// Goroutine with closure
for i := 0; i < 5; i++ {
go func(n int) {
fmt.Println(n)
}(i)
// Pass i as parameter to avoid closure issues
}
Goroutines are lightweight threads managed by the Go runtime. Use 'go' keyword to start a new goroutine.
Channels
ch := make(chan int)
// Send to channel (blocks until received)
go func() {
ch <- 42
}()
// Receive from channel (blocks until sent)
value := <-ch
fmt.Println(value) // 42
// Buffered channel
buffered := make(chan string, 2)
buffered <- "first"
buffered <- "second"
// buffered <- "third" // Would block (buffer full)
fmt.Println(<-buffered) // first
fmt.Println(<-buffered) // second
// Channel direction (in function signatures)
func producer(ch chan<- int) { // Send-only
ch <- 1
}
func consumer(ch <-chan int) { // Receive-only
value := <-ch
}
Channels are typed conduits for Goroutine communication. Can be unbuffered (synchronous) or buffered (asynchronous).
Select Statement
func worker(ch1, ch2 chan string) {
for {
select {
case msg1 := <-ch1:
fmt.Println("From ch1:", msg1)
case msg2 := <-ch2:
fmt.Println("From ch2:", msg2)
case <-time.After(1 * time.Second):
fmt.Println("timeout")
return
}
}
}
// Default case (non-blocking)
select {
case msg := <-ch:
fmt.Println("Received:", msg)
default:
fmt.Println("No message received")
}
// Select for sending
select {
case ch <- value:
fmt.Println("Sent value")
case <-time.After(100 * time.Millisecond):
fmt.Println("Send timeout")
}
Select waits on multiple channel operations. It blocks until one of its cases can run, then executes that case.
Sync Package
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
}
// Mutex for shared data
type SafeCounter struct {
mu sync.Mutex
value int
}
func (c *SafeCounter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
Sync package provides synchronization primitives: WaitGroup for waiting on goroutines, Mutex for mutual exclusion, and more.
Context Package
func worker(ctx context.Context) {
select {
case <-time.After(2 * time.Second):
fmt.Println("Work done")
case <-ctx.Done():
fmt.Println("Cancelled:", ctx.Err())
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
go worker(ctx)
select {
case <-ctx.Done():
fmt.Println("Main: timeout reached")
}
}
// Context with value
ctx := context.WithValue(context.Background(), "key", "value")
if v := ctx.Value("key"); v != nil {
fmt.Println("Value:", v)
}
Context carries deadlines, cancellation signals, and request-scoped values across API boundaries and between goroutines.
Patterns: Worker Pools
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, j)
time.Sleep(time.Second)
results <- j * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// Start 3 workers
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// Send 9 jobs
for j := 1; j <= 9; j++ {
jobs <- j
}
close(jobs)
// Collect results
for a := 1; a <= 9; a++ {
<-results
}
}
Worker pools use multiple goroutines to process work from a queue. Useful for controlling concurrency and resource usage.
Collections
Arrays
var a [3]int // Zero-valued array
b := [3]int{1, 2, 3} // Literal
c := [...]int{1, 2, 3, 4} // Compiler counts size
d := [2][3]int{{1, 2, 3}, {{4, 5, 6} // 2D array
// Access and modify
a[0] = 10
value := b[1] // 2
// Length
length := len(c) // 4
// Iteration
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
for i, v := range b {
fmt.Printf("Index %d: Value %d\n", i, v)
}
// Arrays are values (copied when assigned)
arr1 := [2]int{1, 2}
arr2 := arr1 // Copy
arr2[0] = 10 // Doesn't affect arr1
Arrays are fixed-size sequences. Size is part of the type. Arrays are values, not references.
Slices
var s1 []int // nil slice
s2 := []int{1, 2, 3} // Literal
s3 := make([]string, 3) // Length 3, capacity 3
s4 := make([]int, 2, 5) // Length 2, capacity 5
// From array
arr := [5]int{1, 2, 3, 4, 5}
s5 := arr[1:4] // [2, 3, 4]
// Slice operations
s2 = append(s2, 4) // [1, 2, 3, 4]
s2 = append(s2, 5, 6) // [1, 2, 3, 4, 5, 6]
s2 = append(s2, s3...) // Append another slice
// Copy slices
src := []int{1, 2, 3}
dst := make([]int, 2)
n := copy(dst, src) // n = 2, dst = [1, 2]
// Slice tricks
s2 = s2[1:] // Remove first element
s2 = s2[:len(s2)-1] // Remove last element
s2 = append(s2[:2], s2[3:]...) // Remove element at index 2
Slices are dynamic arrays with length and capacity. They reference underlying arrays. Use make() to create with specific capacity.
Maps
var m1 map[string]int // nil map
m2 := make(map[string]int) // Empty map
m3 := map[string]int{
"apple": 5,
"banana": 10,
}
// Operations
m2["key"] = 1 // Insert or update
value := m2["key"] // Retrieve
delete(m2, "key") // Delete
// Check if key exists
v, ok := m2["key"]
if ok {
fmt.Println("Value:", v)
} else {
fmt.Println("Key not found")
}
// Iteration
for k, v := range m3 {
fmt.Println(k, v)
}
// Length
length := len(m3)
Maps are unordered key-value collections. Keys must be comparable (no slices, maps, or functions).
Sorting
ints := []int{4, 2, 8, 1}
sort.Ints(ints) // [1, 2, 4, 8]
strings := []string{"banana", "apple", "cherry"}
sort.Strings(strings) // ["apple", "banana", "cherry"]
// Custom sorting with sort.Interface
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}
sort.Sort(ByAge(people))
// sort.Slice (Go 1.8+)
sort.Slice(people, func(i, j int) bool {
return people[i].Name < people[j].Name
})
Sort package provides sorting for built-in types. Implement sort.Interface or use sort.Slice for custom sorting.
Container Package
list := list.New()
list.PushBack(1)
list.PushFront(2)
for e := list.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
// Ring (circular list)
r := ring.New(3)
for i := 0; i < 3; i++ {
r.Value = i
r = r.Next()
}
r.Do(func(x interface{}) {
fmt.Println(x)
})
// Heap (priority queue)
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) {
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
Container package provides list (doubly linked list), ring (circular list), and heap (priority queue) implementations.
Set Implementation
type Set map[string]struct{}
func NewSet() Set {
return make(Set)
}
func (s Set) Add(value string) {
s[value] = struct{}{}
}
func (s Set) Remove(value string) {
delete(s, value)
}
func (s Set) Contains(value string) bool {
_, exists := s[value]
return exists
}
// Using the set
s := NewSet()
s.Add("apple")
s.Add("banana")
if s.Contains("apple") {
fmt.Println("Set contains apple")
}
// Generic set (Go 1.18+)
type Set[T comparable] map[T]struct{}
Go doesn't have built-in sets. Implement using maps with empty struct values (zero memory) as the most efficient approach.
Standard Library
fmt Package
fmt.Print("Hello") // No newline
fmt.Println("Hello") // With newline
fmt.Printf("Name: %s, Age: %d", name, age)
// String formatting
s := fmt.Sprintf("Value: %v", data)
fmt.Fprintf(os.Stdout, "Writing: %s\n", text)
// Common format verbs
fmt.Printf("%v", value) // Default format
fmt.Printf("%+v", value) // Add field names (structs)
fmt.Printf("%#v", value) // Go syntax
fmt.Printf("%T", value) // Type
fmt.Printf("%.2f", 3.14159) // Float precision
// Scanning input
var input string
fmt.Scan(&input)
fmt.Scanf("%s %d", &name, &age)
fmt.Scanln(&input)
fmt package provides formatted I/O. Use Print for output, Scan for input, Sprintf for string formatting.
os & io Packages
file, err := os.Open("file.txt")
defer file.Close()
data := []byte("Hello")
err = os.WriteFile("output.txt", data, 0644)
content, err := os.ReadFile("file.txt")
// Environment variables
path := os.Getenv("PATH")
os.Setenv("KEY", "value")
// Command-line arguments
args := os.Args
if len(args) > 1 {
fmt.Println("First arg:", args[1])
}
// io utilities
n, err := io.Copy(dst, src)
all, err := io.ReadAll(reader)
io.WriteString(writer, "text")
os provides OS interface (files, env vars, args). io provides basic I/O interfaces and utilities.
net/http Package
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
// HTTP client
resp, err := http.Get("https://api.example.com")
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
// POST request
jsonData := []byte(`{"name":"John"}`)
resp, err := http.Post("https://api.example.com",
"application/json", bytes.NewBuffer(jsonData))
// Custom client with timeout
client := &http.Client{
Timeout: 10 * time.Second,
}
req, _ := http.NewRequest("GET", "https://example.com", nil)
resp, err := client.Do(req)
net/http provides HTTP client and server implementations. Built-in routing, headers, cookies, and file serving.
encoding/json
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
p := Person{Name: "Alice", Age: 30}
jsonData, err := json.Marshal(p)
// {"name":"Alice","age":30}
// Pretty printing
jsonData, err := json.MarshalIndent(p, "", " ")
// Unmarshaling (JSON to struct)
var p2 Person
err := json.Unmarshal(jsonData, &p2)
// Streaming encoder/decoder
enc := json.NewEncoder(os.Stdout)
enc.Encode(p)
dec := json.NewDecoder(os.Stdin)
var data map[string]interface{}{}
dec.Decode(&data)
encoding/json provides JSON marshaling and unmarshaling. Use struct tags to customize field names and behavior.
time Package
now := time.Now()
fmt.Println(now.Year(), now.Month(), now.Day())
// Creating time
t := time.Date(2023, time.January, 10, 23, 0, 0, 0, time.UTC)
// Parsing time
t, err := time.Parse("2006-01-02", "2023-01-10")
t, err := time.Parse(time.RFC3339, "2023-01-10T15:30:00Z")
// Formatting time
formatted := now.Format("2006-01-02 15:04:05")
formatted := now.Format(time.RFC1123)
// Duration
duration := 2 * time.Hour + 30 * time.Minute
later := now.Add(duration)
earlier := now.Add(-duration)
// Timer and Ticker
timer := time.NewTimer(2 * time.Second)
<-timer.C
ticker := time.NewTicker(1 * time.Second)
for t := range ticker.C {
fmt.Println("Tick at", t)
}
time package provides time measurement and display. Note: Use "2006-01-02 15:04:05" as reference time for formatting.
Other Useful Packages
s := strconv.Itoa(123) // "123"
i, err := strconv.Atoi("456") // 456
b, err := strconv.ParseBool("true")
f, err := strconv.ParseFloat("3.14", 64)
// strings - string manipulation
contains := strings.Contains("hello", "ell")
count := strings.Count("cheese", "e")
fields := strings.Fields("a b c") // ["a", "b", "c"]
lower := strings.ToLower("HELLO")
replaced := strings.Replace("foo", "o", "a", -1)
// regexp - regular expressions
matched, err := regexp.MatchString("^[a-z]+$", "hello")
re := regexp.MustCompile(`^\d+$`)
matches := re.FindString("123 abc") // "123"
// path/filepath - file paths
dir := filepath.Dir("/home/user/file.txt")
base := filepath.Base("/home/user/file.txt")
ext := filepath.Ext("file.txt")
abs, err := filepath.Abs("file.txt")
Go's standard library is extensive. Key packages include strconv, strings, regexp, path/filepath, and many more for common tasks.
• Use gofmt to format code automatically
• Handle errors explicitly (don't ignore them)
• Prefer composition over inheritance
• Return errors, don't panic (except for unrecoverable errors)
• Use interfaces for abstraction and testing
• Keep packages small and focused on single responsibility
• Write tests (use testing package)
• Use go vet and staticcheck for code analysis
• Document exported functions and types with comments