Go
Basic Syntax
Hello World
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Running Go Programs
go run main.go # Run program
go build main.go # Compile to binary
go build -o app main.go # Compile with custom name
go install # Install binary to $GOPATH/bin
go fmt ./... # Format all files
go vet ./... # Check for common mistakes
go test ./... # Run tests
go mod init module-name # Initialize module
go mod tidy # Clean up dependencies
go get package-name # Download dependency
Variables & Constants
Variable Declaration
// Explicit type
var name string = "John"
var age int = 30
var isActive bool = true
// Type inference
var name = "John"
var age = 30
// Short declaration (inside functions only)
name := "John"
age := 30
// Multiple variables
var x, y int = 1, 2
a, b := 3, 4
var (
name string = "John"
age int = 30
)
// Zero values (default values)
var s string // ""
var i int // 0
var f float64 // 0.0
var b bool // false
var p *int // nil
Constants
const Pi = 3.14159
const Greeting = "Hello"
// Multiple constants
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
// iota (auto-incrementing)
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
)
const (
_ = iota // Skip 0
KB = 1 << (10 * iota) // 1024
MB // 1048576
GB // 1073741824
)
Data Types
Basic Types
// Boolean
bool
// String
string
// Integers
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
// Byte (alias for uint8)
byte
// Rune (alias for int32, represents Unicode code point)
rune
// Floating point
float32 float64
// Complex numbers
complex64 complex128
Type Conversion
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
// String conversions
s := string(65) // "A" (from ASCII)
s = strconv.Itoa(42) // "42"
i, _ = strconv.Atoi("42") // 42
// Other conversions
f, _ = strconv.ParseFloat("3.14", 64)
b, _ = strconv.ParseBool("true")
Operators
Arithmetic Operators
+ // Addition
- // Subtraction
* // Multiplication
/ // Division
% // Modulus
++ // Increment (postfix only)
-- // Decrement (postfix only)
Comparison Operators
== // Equal to
!= // Not equal to
< // Less than
> // Greater than
<= // Less than or equal to
>= // Greater than or equal to
Logical Operators
&& // Logical AND
|| // Logical OR
! // Logical NOT
Bitwise Operators
& // AND
| // OR
^ // XOR
<< // Left shift
>> // Right shift
&^ // AND NOT
Control Flow
If-Else
// Basic if
if x > 0 {
fmt.Println("Positive")
}
// If-else
if x > 0 {
fmt.Println("Positive")
} else {
fmt.Println("Non-positive")
}
// If-else if-else
if x > 0 {
fmt.Println("Positive")
} else if x < 0 {
fmt.Println("Negative")
} else {
fmt.Println("Zero")
}
// If with initialization
if err := doSomething(); err != nil {
fmt.Println("Error:", err)
}
// Short circuit evaluation
if user != nil && user.IsActive {
// user.IsActive only evaluated if user != nil
}
Switch
// Basic switch
switch day {
case "Monday":
fmt.Println("Start of week")
case "Friday":
fmt.Println("End of week")
default:
fmt.Println("Middle of week")
}
// Switch with multiple cases
switch day {
case "Saturday", "Sunday":
fmt.Println("Weekend")
default:
fmt.Println("Weekday")
}
// Switch with initialization
switch err := doSomething(); err {
case nil:
fmt.Println("Success")
default:
fmt.Println("Error:", err)
}
// Switch without condition (like if-else)
switch {
case x < 0:
fmt.Println("Negative")
case x > 0:
fmt.Println("Positive")
default:
fmt.Println("Zero")
}
// Type switch
switch v := i.(type) {
case int:
fmt.Printf("Integer: %d\n", v)
case string:
fmt.Printf("String: %s\n", v)
default:
fmt.Printf("Unknown type\n")
}
// Fallthrough
switch x {
case 1:
fmt.Println("One")
fallthrough
case 2:
fmt.Println("Two or less")
}
Loops
// Basic for loop
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// While-style loop
i := 0
for i < 10 {
fmt.Println(i)
i++
}
// Infinite loop
for {
// Break to exit
if condition {
break
}
}
// Range over slice/array
nums := []int{1, 2, 3, 4, 5}
for index, value := range nums {
fmt.Printf("Index: %d, Value: %d\n", index, value)
}
// Range (ignore index)
for _, value := range nums {
fmt.Println(value)
}
// Range (index only)
for index := range nums {
fmt.Println(index)
}
// Range over map
m := map[string]int{"a": 1, "b": 2}
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
// Range over string (iterates over runes)
for index, char := range "Hello" {
fmt.Printf("%d: %c\n", index, char)
}
// Break and continue
for i := 0; i < 10; i++ {
if i == 5 {
break // Exit loop
}
if i%2 == 0 {
continue // Skip to next iteration
}
fmt.Println(i)
}
// Labeled break/continue
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i*j > 3 {
break outer
}
fmt.Printf("%d * %d = %d\n", i, j, i*j)
}
}
Defer, Panic, Recover
// Defer (executes after function returns, LIFO order)
func example() {
defer fmt.Println("Third")
defer fmt.Println("Second")
fmt.Println("First")
// Output: First, Second, Third
}
// Defer with cleanup
func readFile() {
f, err := os.Open("file.txt")
if err != nil {
return
}
defer f.Close() // Ensures file is closed
// Read file...
}
// Panic (stop execution, run defers, propagate up)
func causesPanic() {
panic("Something went wrong!")
}
// Recover (catch panic, only works in defer)
func safeFunction() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from:", r)
}
}()
// Code that might panic
causesPanic()
fmt.Println("This won't execute")
}
Functions
Basic Functions
// No parameters, no return
func sayHello() {
fmt.Println("Hello")
}
// With parameters
func greet(name string) {
fmt.Println("Hello", name)
}
// Multiple parameters
func add(a int, b int) int {
return a + b
}
// Same type parameters (shorthand)
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 split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // naked return
}
// Variadic functions
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
// Call: sum(1, 2, 3, 4)
// Function as parameter
func apply(f func(int) int, value int) int {
return f(value)
}
// Anonymous function
func() {
fmt.Println("Anonymous")
}()
// Function variable
var multiply = func(a, b int) int {
return a * b
}
// Closure
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
// Usage:
// c := counter()
// c() // 1
// c() // 2
Methods
// Method (function with receiver)
type Rectangle struct {
Width, Height float64
}
// Value receiver (receives copy)
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// Pointer receiver (can modify original)
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
// Usage
rect := Rectangle{Width: 10, Height: 5}
area := rect.Area() // 50
rect.Scale(2) // Modifies rect
Arrays & Slices
Arrays
// Fixed size, cannot be resized
var arr [5]int // [0 0 0 0 0]
arr := [5]int{1, 2, 3, 4, 5} // [1 2 3 4 5]
arr := [...]int{1, 2, 3} // Size inferred: [1 2 3]
arr := [5]int{0: 1, 4: 5} // [1 0 0 0 5]
// Access elements
arr[0] = 10
val := arr[0]
// Length
len(arr) // 5
// Iterate
for i, v := range arr {
fmt.Printf("Index %d: %d\n", i, v)
}
Slices
// Dynamic size, reference to underlying array
var s []int // nil slice
s := []int{1, 2, 3} // [1 2 3]
s := make([]int, 5) // [0 0 0 0 0] (length 5)
s := make([]int, 5, 10) // length 5, capacity 10
// Slice from array/slice
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4] // [2 3 4] (index 1 to 3)
s := arr[:3] // [1 2 3] (start to index 2)
s := arr[2:] // [3 4 5] (index 2 to end)
s := arr[:] // [1 2 3 4 5] (entire array)
// Append
s = append(s, 6) // Add one element
s = append(s, 7, 8, 9) // Add multiple
s = append(s, anotherSlice...) // Add another slice
// Copy
dest := make([]int, len(s))
copy(dest, s)
// Length and capacity
len(s) // Number of elements
cap(s) // Capacity of underlying array
// 2D slice
matrix := [][]int{
{1, 2, 3},
{4, 5, 6},
}
// Common operations
// Remove element at index i
s = append(s[:i], s[i+1:]...)
// Insert element at index i
s = append(s[:i], append([]int{newElement}, s[i:]...)...)
// Check if slice is empty
if len(s) == 0 { }
Maps
Basic Map Operations
// Declaration
var m map[string]int // nil map
m := make(map[string]int) // Empty map
m := map[string]int{"a": 1, "b": 2} // Initialized map
// Set value
m["key"] = 42
// Get value
value := m["key"]
// Check if key exists
value, exists := m["key"]
if exists {
fmt.Println("Found:", value)
}
// Delete key
delete(m, "key")
// Length
len(m)
// Iterate
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
// Keys only
for key := range m {
fmt.Println(key)
}
// Check if map is empty
if len(m) == 0 { }
// Map of maps
m := make(map[string]map[string]int)
m["outer"] = make(map[string]int)
m["outer"]["inner"] = 42
Structs
Basic Structs
// Define struct
type Person struct {
Name string
Age int
Email string
}
// Create instance
p1 := Person{Name: "John", Age: 30, Email: "john@example.com"}
p2 := Person{"Jane", 25, "jane@example.com"} // Order matters
p3 := Person{Name: "Bob"} // Other fields get zero values
// Anonymous struct
person := struct {
Name string
Age int
}{
Name: "John",
Age: 30,
}
// Access fields
p1.Name = "John Doe"
age := p1.Age
// Pointer to struct
p := &Person{Name: "John"}
p.Name = "Jane" // No need to dereference
// Embedded structs (composition)
type Address struct {
Street string
City string
}
type Employee struct {
Person // Embedded
Address // Embedded
Position string
}
e := Employee{
Person: Person{Name: "John", Age: 30},
Address: Address{Street: "123 Main", City: "NYC"},
Position: "Developer",
}
// Access embedded fields
e.Name = "Jane" // Direct access
e.Person.Name = "Jane" // Explicit access
// Struct tags (for JSON, etc.)
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Password string `json:"-"` // Ignored in JSON
}
// Anonymous fields
type Employee struct {
string // Anonymous field
int // Anonymous field
}
Struct Methods
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
// Constructor pattern
func NewRectangle(width, height float64) *Rectangle {
return &Rectangle{
Width: width,
Height: height,
}
}
Interfaces
Basic Interfaces
// Define interface
type Shape interface {
Area() float64
Perimeter() float64
}
// Implement interface (implicit)
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)
}
// Use interface
func printShapeInfo(s Shape) {
fmt.Printf("Area: %.2f\n", s.Area())
fmt.Printf("Perimeter: %.2f\n", s.Perimeter())
}
// Empty interface (accepts any type)
func printAnything(v interface{}) {
fmt.Println(v)
}
// Type assertion
var i interface{} = "hello"
s := i.(string) // Assert i is string
s, ok := i.(string) // Safe assertion
// Type switch
func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Integer: %d\n", v)
case string:
fmt.Printf("String: %s\n", v)
case bool:
fmt.Printf("Boolean: %t\n", v)
default:
fmt.Printf("Unknown type\n")
}
}
// Common interfaces
type Stringer interface {
String() string // For fmt.Println
}
type Error interface {
Error() string // For error handling
}
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
Pointers
// Pointer basics
var p *int // Pointer to int
i := 42
p = &i // Address of i
*p = 21 // Dereference and set value
fmt.Println(*p) // Dereference and read value
// Zero value of pointer is nil
var p *int
if p == nil {
fmt.Println("Pointer is nil")
}
// new() allocates memory
p := new(int) // Returns *int pointing to zero value
*p = 42
// Pointers to structs
type Person struct {
Name string
}
p := &Person{Name: "John"}
p.Name = "Jane" // Automatic dereferencing
// Pass by value vs pointer
func modifyValue(x int) {
x = 100 // Doesn't affect original
}
func modifyPointer(x *int) {
*x = 100 // Modifies original
}
i := 42
modifyValue(i) // i is still 42
modifyPointer(&i) // i is now 100
Error Handling
// errors package
import "errors"
// Create error
err := errors.New("something went wrong")
err := fmt.Errorf("error: %s", "details")
// Return error
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// Check error
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
// Custom error type
type MyError struct {
Code int
Message string
}
func (e *MyError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}
// Error wrapping (Go 1.13+)
import "fmt"
err := doSomething()
if err != nil {
return fmt.Errorf("failed to do something: %w", err)
}
// Unwrap error
import "errors"
var ErrNotFound = errors.New("not found")
err := findUser()
if errors.Is(err, ErrNotFound) {
// Handle not found
}
// Type assertion for errors
var myErr *MyError
if errors.As(err, &myErr) {
fmt.Println("Custom error code:", myErr.Code)
}
// Panic for unrecoverable errors
if criticalError {
panic("unrecoverable error")
}
Goroutines & Concurrency
Goroutines
// Start goroutine
go functionName()
go func() {
fmt.Println("Anonymous goroutine")
}()
// Example
func printNumbers() {
for i := 0; i < 5; i++ {
fmt.Println(i)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
go printNumbers() // Runs concurrently
printNumbers() // Runs in main goroutine
}
Channels
// Create channel
ch := make(chan int) // Unbuffered
ch := make(chan int, 10) // Buffered (capacity 10)
// Send to channel
ch <- 42
// Receive from channel
value := <-ch
// Close channel
close(ch)
// Check if channel is closed
value, ok := <-ch
if !ok {
fmt.Println("Channel closed")
}
// Range over channel
for value := range ch {
fmt.Println(value)
}
// Select statement (multiplex channels)
select {
case msg := <-ch1:
fmt.Println("Received from ch1:", msg)
case msg := <-ch2:
fmt.Println("Received from ch2:", msg)
case ch3 <- 42:
fmt.Println("Sent to ch3")
default:
fmt.Println("No communication")
}
// Timeout with select
select {
case msg := <-ch:
fmt.Println("Received:", msg)
case <-time.After(1 * time.Second):
fmt.Println("Timeout")
}
// Worker pool pattern
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
results <- j * 2
}
}
jobs := make(chan int, 100)
results := make(chan int, 100)
// Start workers
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// Send jobs
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
// Collect results
for a := 1; a <= 5; a++ {
<-results
}
Synchronization
import "sync"
// 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() // Wait for all goroutines
// Mutex
var mu sync.Mutex
var counter int
func increment() {
mu.Lock()
counter++
mu.Unlock()
}
// RWMutex (multiple readers, single writer)
var rwmu sync.RWMutex
var data map[string]string
func read(key string) string {
rwmu.RLock()
defer rwmu.RUnlock()
return data[key]
}
func write(key, value string) {
rwmu.Lock()
defer rwmu.Unlock()
data[key] = value
}
// Once (execute only once)
var once sync.Once
func initialize() {
once.Do(func() {
fmt.Println("Initialized")
})
}
// Atomic operations
import "sync/atomic"
var counter int64
atomic.AddInt64(&counter, 1)
value := atomic.LoadInt64(&counter)
atomic.StoreInt64(&counter, 0)
Packages & Imports
Package Structure
// In file math/math.go
package math
// Exported (starts with capital letter)
func Add(a, b int) int {
return a + b
}
// Unexported (starts with lowercase)
func subtract(a, b int) int {
return a - b
}
// In main.go
package main
import "yourmodule/math"
func main() {
result := math.Add(5, 3)
}
Import Variations
// Standard import
import "fmt"
import "os"
// Multiple imports
import (
"fmt"
"os"
"strings"
)
// Alias import
import f "fmt"
f.Println("Hello")
// Dot import (not recommended)
import . "fmt"
Println("Hello") // No package prefix needed
// Blank import (for side effects only)
import _ "github.com/lib/pq"
Init Function
package mypackage
func init() {
// Runs automatically before main()
// Used for initialization
fmt.Println("Package initialized")
}
File I/O
Reading Files
import (
"io/ioutil"
"os"
"bufio"
)
// Read entire file
data, err := ioutil.ReadFile("file.txt")
if err != nil {
panic(err)
}
fmt.Println(string(data))
// Read file line by line
file, err := os.Open("file.txt")
if err != nil {
panic(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// Read with buffer
file, _ := os.Open("file.txt")
defer file.Close()
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
fmt.Print(line)
}
Writing Files
// Write entire file
data := []byte("Hello, World!")
err := ioutil.WriteFile("file.txt", data, 0644)
// Write with os.Create
file, err := os.Create("file.txt")
if err != nil {
panic(err)
}
defer file.Close()
file.WriteString("Hello, World!\n")
// Buffered writer
file, _ := os.Create("file.txt")
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString("Hello, World!\n")
writer.Flush() // Important!
// Append to file
file, err := os.OpenFile("file.txt",
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer file.Close()
file.WriteString("Appended text\n")
File Operations
// Check if file exists
if _, err := os.Stat("file.txt"); os.IsNotExist(err) {
fmt.Println("File does not exist")
}
// Delete file
err := os.Remove("file.txt")
// Rename file
err := os.Rename("old.txt", "new.txt")
// Create directory
err := os.Mkdir("mydir", 0755)
err := os.MkdirAll("path/to/dir", 0755) // Create parent dirs
// Remove directory
err := os.Remove("mydir")
err := os.RemoveAll("mydir") // Recursive
// List directory
files, err := ioutil.ReadDir(".")
for _, file := range files {
fmt.Println(file.Name(), file.IsDir())
}
// Walk directory tree
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
fmt.Println(path)
return nil
})
JSON
Encoding & Decoding
import "encoding/json"
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email,omitempty"` // Omit if empty
}
// Marshal (Go to JSON)
person := Person{Name: "John", Age: 30}
jsonData, err := json.Marshal(person)
if err != nil {
panic(err)
}
fmt.Println(string(jsonData)) // {"name":"John","age":30}
// Marshal with indentation
jsonData, err := json.MarshalIndent(person, "", " ")
// Unmarshal (JSON to Go)
jsonString := `{"name":"John","age":30}`
var person Person
err := json.Unmarshal([]byte(jsonString), &person)
// Unmarshal to map
var result map[string]interface{}
json.Unmarshal([]byte(jsonString), &result)
// Encode to writer
file, _ := os.Create("data.json")
defer file.Close()
encoder := json.NewEncoder(file)
encoder.Encode(person)
// Decode from reader
file, _ := os.Open("data.json")
defer file.Close()
decoder := json.NewDecoder(file)
var person Person
decoder.Decode(&person)
Testing
Basic Tests
// In math.go
package math
func Add(a, b int) int {
return a + b
}
// In math_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}
// Table-driven tests
func TestAddTable(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive", 2, 3, 5},
{"negative", -1, -1, -2},
{"zero", 0, 0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("got %d, want %d", result, tt.expected)
}
})
}
}
// Benchmark
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}
// Example (for documentation)
func ExampleAdd() {
fmt.Println(Add(2, 3))
// Output: 5
}
// Setup and teardown
func TestMain(m *testing.M) {
// Setup
fmt.Println("Setting up tests")
// Run tests
code := m.Run()
// Teardown
fmt.Println("Cleaning up")
os.Exit(code)
}
// Run tests
go test # Run tests in current package
go test ./... # Run all tests in module
go test -v # Verbose output
go test -run TestAdd # Run specific test
go test -bench . # Run benchmarks
go test -cover # Show coverage
go test -coverprofile=c.out # Generate coverage profile
go tool cover -html=c.out # View coverage in browser
HTTP & Web
HTTP Server
import (
"fmt"
"net/http"
"encoding/json"
)
// Basic server
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
// JSON response
type Response struct {
Message string `json:"message"`
Status int `json:"status"`
}
func jsonHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
response := Response{Message: "Success", Status: 200}
json.NewEncoder(w).Encode(response)
}
// Handle different methods
func userHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
// Handle GET
fmt.Fprintf(w, "GET request")
case "POST":
// Handle POST
fmt.Fprintf(w, "POST request")
case "PUT":
// Handle PUT
fmt.Fprintf(w, "PUT request")
case "DELETE":
// Handle DELETE
fmt.Fprintf(w, "DELETE request")
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
// Read request body
func postHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var data map[string]interface{}
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&data); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
fmt.Fprintf(w, "Received: %v", data)
}
// URL parameters
func paramHandler(w http.ResponseWriter, r *http.Request) {
// Query parameters: /api?name=John&age=30
name := r.URL.Query().Get("name")
age := r.URL.Query().Get("age")
fmt.Fprintf(w, "Name: %s, Age: %s", name, age)
}
// Middleware
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("%s %s\n", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
})
}
// Use middleware
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", handler)
loggedMux := loggingMiddleware(mux)
http.ListenAndServe(":8080", loggedMux)
}
// Custom server with timeouts
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
server.ListenAndServe()
// Serve static files
http.Handle("/static/", http.StripPrefix("/static/",
http.FileServer(http.Dir("./static"))))
// Redirect
func redirectHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/new-path", http.StatusMovedPermanently)
}
HTTP Client
import (
"bytes"
"io/ioutil"
"net/http"
"time"
)
// GET request
resp, err := http.Get("https://api.example.com/data")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
// POST request with JSON
data := map[string]string{"name": "John", "email": "john@example.com"}
jsonData, _ := json.Marshal(data)
resp, err := http.Post("https://api.example.com/users",
"application/json",
bytes.NewBuffer(jsonData))
// Custom request
req, err := http.NewRequest("PUT", "https://api.example.com/users/1",
bytes.NewBuffer(jsonData))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer token123")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
// Custom client with timeout
client := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
},
}
// Download file
resp, err := http.Get("https://example.com/file.pdf")
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, err := os.Create("file.pdf")
if err != nil {
panic(err)
}
defer out.Close()
io.Copy(out, resp.Body)
Context
import "context"
// Background context (never cancelled)
ctx := context.Background()
// TODO context (placeholder)
ctx := context.TODO()
// With cancel
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Always call cancel
go func() {
time.Sleep(2 * time.Second)
cancel() // Cancel after 2 seconds
}()
select {
case <-ctx.Done():
fmt.Println("Context cancelled:", ctx.Err())
}
// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// With deadline
deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
// With value (for request-scoped data)
type key string
const userKey key = "user"
ctx := context.WithValue(context.Background(), userKey, "john")
user := ctx.Value(userKey).(string)
// Use context in HTTP request
req, _ := http.NewRequestWithContext(ctx, "GET",
"https://api.example.com/data", nil)
resp, err := client.Do(req)
// Use context in database query
rows, err := db.QueryContext(ctx, "SELECT * FROM users")
// Context in function
func doWork(ctx context.Context) error {
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
// Do work
time.Sleep(100 * time.Millisecond)
}
}
}
Database (SQL)
import (
"database/sql"
_ "github.com/lib/pq" // PostgreSQL driver
)
// Connect to database
db, err := sql.Open("postgres",
"host=localhost port=5432 user=postgres password=secret dbname=mydb sslmode=disable")
if err != nil {
panic(err)
}
defer db.Close()
// Test connection
err = db.Ping()
if err != nil {
panic(err)
}
// Set connection pool settings
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
// Query single row
var name string
var age int
err = db.QueryRow("SELECT name, age FROM users WHERE id = $1", 1).Scan(&name, &age)
if err != nil {
if err == sql.ErrNoRows {
fmt.Println("No rows found")
} else {
panic(err)
}
}
// Query multiple rows
rows, err := db.Query("SELECT id, name, age FROM users WHERE age > $1", 18)
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
var age int
err = rows.Scan(&id, &name, &age)
if err != nil {
panic(err)
}
fmt.Printf("ID: %d, Name: %s, Age: %d\n", id, name, age)
}
// Check for errors after iteration
if err = rows.Err(); err != nil {
panic(err)
}
// Insert
result, err := db.Exec("INSERT INTO users (name, age) VALUES ($1, $2)",
"John", 30)
if err != nil {
panic(err)
}
// Get last insert ID (if supported)
id, err := result.LastInsertId()
// Get rows affected
rowsAffected, err := result.RowsAffected()
// Update
result, err := db.Exec("UPDATE users SET age = $1 WHERE id = $2", 31, 1)
// Delete
result, err := db.Exec("DELETE FROM users WHERE id = $1", 1)
// Prepared statements
stmt, err := db.Prepare("INSERT INTO users (name, age) VALUES ($1, $2)")
if err != nil {
panic(err)
}
defer stmt.Close()
for i := 0; i < 10; i++ {
_, err = stmt.Exec(fmt.Sprintf("User%d", i), 20+i)
if err != nil {
panic(err)
}
}
// Transactions
tx, err := db.Begin()
if err != nil {
panic(err)
}
_, err = tx.Exec("INSERT INTO users (name, age) VALUES ($1, $2)", "John", 30)
if err != nil {
tx.Rollback()
panic(err)
}
_, err = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE id = $1", 1)
if err != nil {
tx.Rollback()
panic(err)
}
err = tx.Commit()
if err != nil {
panic(err)
}
// NULL handling
var name sql.NullString
var age sql.NullInt64
err = db.QueryRow("SELECT name, age FROM users WHERE id = $1", 1).Scan(&name, &age)
if name.Valid {
fmt.Println("Name:", name.String)
} else {
fmt.Println("Name is NULL")
}
Command-Line Arguments
import (
"flag"
"fmt"
"os"
)
// Using flag package
var (
name = flag.String("name", "World", "Name to greet")
age = flag.Int("age", 0, "Your age")
verbose = flag.Bool("verbose", false, "Verbose output")
)
func main() {
flag.Parse()
fmt.Printf("Hello, %s!\n", *name)
fmt.Printf("Age: %d\n", *age)
if *verbose {
fmt.Println("Verbose mode enabled")
}
// Non-flag arguments
args := flag.Args()
fmt.Println("Other arguments:", args)
}
// Run: go run main.go -name=John -age=30 -verbose arg1 arg2
// Using os.Args directly
func main() {
// os.Args[0] is program name
// os.Args[1:] are arguments
if len(os.Args) < 2 {
fmt.Println("Usage: program <arg1> <arg2>")
os.Exit(1)
}
fmt.Println("Arguments:", os.Args[1:])
}
String Manipulation
import "strings"
// Common string operations
strings.Contains("hello", "ll") // true
strings.HasPrefix("hello", "he") // true
strings.HasSuffix("hello", "lo") // true
strings.Index("hello", "ll") // 2
strings.Count("cheese", "e") // 3
strings.Replace("hello", "l", "L", 2) // "heLLo"
strings.ReplaceAll("hello", "l", "L") // "heLLo"
strings.ToUpper("hello") // "HELLO"
strings.ToLower("HELLO") // "hello"
strings.Title("hello world") // "Hello World"
strings.TrimSpace(" hello ") // "hello"
strings.Trim("!!!hello!!!", "!") // "hello"
strings.TrimLeft("!!!hello!!!", "!") // "hello!!!"
strings.TrimRight("!!!hello!!!", "!") // "!!!hello"
strings.Split("a,b,c", ",") // ["a", "b", "c"]
strings.Join([]string{"a", "b"}, ",") // "a,b"
strings.Repeat("ha", 3) // "hahaha"
// String builder (efficient concatenation)
var sb strings.Builder
sb.WriteString("Hello")
sb.WriteString(" ")
sb.WriteString("World")
result := sb.String() // "Hello World"
// Format strings
s := fmt.Sprintf("Name: %s, Age: %d", "John", 30)
Regular Expressions
import "regexp"
// Compile pattern
re := regexp.MustCompile(`\d+`) // One or more digits
// Match
matched := re.MatchString("abc123") // true
// Find
result := re.FindString("abc123def456") // "123"
// Find all
results := re.FindAllString("abc123def456", -1) // ["123", "456"]
// Replace
result := re.ReplaceAllString("abc123def456", "X") // "abcXdefX"
// Capture groups
re := regexp.MustCompile(`(\d+)-(\d+)-(\d+)`)
matches := re.FindStringSubmatch("2024-01-15")
// matches[0] = "2024-01-15" (full match)
// matches[1] = "2024"
// matches[2] = "01"
// matches[3] = "15"
// Named capture groups
re := regexp.MustCompile(`(?P<year>\d+)-(?P<month>\d+)-(?P<day>\d+)`)
matches := re.FindStringSubmatch("2024-01-15")
names := re.SubexpNames()
result := make(map[string]string)
for i, name := range names {
if i > 0 && name != "" {
result[name] = matches[i]
}
}
// result["year"] = "2024"
// result["month"] = "01"
// result["day"] = "15"
Time & Date
import "time"
// Current time
now := time.Now()
// Create specific time
t := time.Date(2024, time.January, 15, 10, 30, 0, 0, time.UTC)
// Parse time string
layout := "2006-01-02 15:04:05"
t, err := time.Parse(layout, "2024-01-15 10:30:00")
// Common layouts
time.RFC3339 // "2006-01-02T15:04:05Z07:00"
time.RFC822 // "02 Jan 06 15:04 MST"
time.Kitchen // "3:04PM"
// Format time
formatted := now.Format("2006-01-02 15:04:05")
formatted := now.Format(time.RFC3339)
// Time components
year := now.Year()
month := now.Month()
day := now.Day()
hour := now.Hour()
minute := now.Minute()
second := now.Second()
weekday := now.Weekday()
// Time arithmetic
tomorrow := now.Add(24 * time.Hour)
yesterday := now.Add(-24 * time.Hour)
nextWeek := now.AddDate(0, 0, 7) // years, months, days
// Duration
duration := 5 * time.Second
duration := 10 * time.Minute
duration := 2 * time.Hour
// Compare times
t1.Before(t2)
t1.After(t2)
t1.Equal(t2)
// Difference between times
diff := t2.Sub(t1) // Returns duration
hours := diff.Hours()
minutes := diff.Minutes()
// Unix timestamp
timestamp := now.Unix() // Seconds since epoch
millis := now.UnixMilli() // Milliseconds
nanos := now.UnixNano() // Nanoseconds
// From Unix timestamp
t := time.Unix(1234567890, 0)
// Sleep
time.Sleep(2 * time.Second)
// Timer (executes once)
timer := time.NewTimer(2 * time.Second)
<-timer.C
fmt.Println("Timer expired")
// Stop timer
timer.Stop()
// Ticker (repeats)
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for t := range ticker.C {
fmt.Println("Tick at", t)
}
// Timeout channel
timeout := time.After(5 * time.Second)
select {
case <-timeout:
fmt.Println("Timeout!")
}
// Time zones
loc, _ := time.LoadLocation("America/New_York")
t := time.Now().In(loc)
Reflection
import "reflect"
// Get type
var x float64 = 3.4
t := reflect.TypeOf(x)
fmt.Println("Type:", t) // float64
// Get value
v := reflect.ValueOf(x)
fmt.Println("Value:", v) // 3.4
fmt.Println("Kind:", v.Kind()) // float64
fmt.Println("Type:", v.Type()) // float64
fmt.Println("Float:", v.Float()) // 3.4
// Check if settable
v := reflect.ValueOf(&x).Elem()
if v.CanSet() {
v.SetFloat(7.1)
}
// Struct reflection
type Person struct {
Name string `json:"name" validate:"required"`
Age int `json:"age"`
}
p := Person{Name: "John", Age: 30}
t := reflect.TypeOf(p)
v := reflect.ValueOf(p)
// Iterate struct fields
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("Field: %s, Type: %s, Value: %v\n",
field.Name, field.Type, value)
// Get struct tag
tag := field.Tag.Get("json")
fmt.Println("JSON tag:", tag)
}
// Call method via reflection
method := v.MethodByName("MethodName")
if method.IsValid() {
result := method.Call([]reflect.Value{})
}
Best Practices
Project Structure
myproject/
├── cmd/ # Main applications
│ └── myapp/
│ └── main.go
├── internal/ # Private application code
│ ├── handlers/
│ ├── models/
│ └── services/
├── pkg/ # Public libraries
│ └── utils/
├── api/ # API definitions (OpenAPI, protobuf)
├── web/ # Static web assets
├── configs/ # Configuration files
├── scripts/ # Build/deploy scripts
├── test/ # Additional test files
├── docs/ # Documentation
├── go.mod # Module definition
├── go.sum # Dependency checksums
├── Makefile # Build commands
└── README.md
Common Idioms
// Error handling
if err != nil {
return err
}
// Defer for cleanup
f, err := os.Open("file.txt")
if err != nil {
return err
}
defer f.Close()
// Empty struct for set
set := make(map[string]struct{})
set["item"] = struct{}{}
_, exists := set["item"]
// Check empty slice/map
if len(slice) == 0 { }
if len(m) == 0 { }
// Avoid nil pointer
if user != nil && user.Address != nil {
fmt.Println(user.Address.Street)
}
// Use named results for documentation
func divide(a, b float64) (result float64, err error) {
if b == 0 {
err = errors.New("division by zero")
return
}
result = a / b
return
}
// Accept interfaces, return structs
func ProcessReader(r io.Reader) (*Result, error) {
// ...
}
Performance Tips
// Preallocate slices when size is known
s := make([]int, 0, 100)
// Use sync.Pool for frequently allocated objects
var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
// Avoid allocations in loops
// Bad: creates new slice each iteration
for i := 0; i < n; i++ {
s := make([]int, size)
}
// Good: reuse slice
s := make([]int, size)
for i := 0; i < n; i++ {
// Clear and reuse s
}
// Use string builder for concatenation
var sb strings.Builder
for _, s := range strings {
sb.WriteString(s)
}
result := sb.String()
// Range over slice copies value, use pointer if large
for i := range largeStructs {
processStruct(&largeStructs[i])
}