Cheatsheets

Go

Go

Go is a statically typed, compiled programming language designed for simplicity, efficiency, and concurrent programming. It's ideal for building fast, scalable server applications and system tools.

7 Categories 21 Sections 62 Examples
Go Golang Programming Functions Interfaces Goroutines Channels Concurrency Error Handling

Getting Started

Fundamental Go concepts and basic syntax for beginners.

Hello World

Basic Go program structure with package declaration and main function.

Simple Hello World

A basic Go program with package declaration, import, and main function that prints a greeting.

Code
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Execution
go run main.go
Output
Hello, World!
  • Every Go program must have a main package and main function.
  • The main function is the entry point of the program.

Multiple imports

Demonstrates grouped imports using parentheses for multiple packages.

Code
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println("Pi is approximately", math.Pi)
}
Execution
go run main.go
Output
Pi is approximately 3.141592653589793
  • Use parentheses to group multiple imports.
  • Import order doesn't matter, but groups are conventional.

Using a custom function

Demonstrates defining and calling a simple function within the main package.

Code
package main
import "fmt"
func greet(name string) {
fmt.Println("Hello,", name)
}
func main() {
greet("Gopher")
}
Execution
go run main.go
Output
Hello, Gopher
  • Functions are declared with the func keyword.
  • Function parameters must include their type.

Variables

Declaring and working with variables in Go, including type inference and shorthand syntax.

Variable declaration with var

Demonstrates explicit variable declaration with type specification.

Code
package main
import "fmt"
func main() {
var name string = "Alice"
var age int = 30
fmt.Println(name, age)
}
Execution
go run main.go
Output
Alice 30
  • var keyword declares a variable with explicit type.
  • Variables must be used after declaration or compilation fails.

Type inference

Go infers the type from the assigned value without explicit type specification.

Code
package main
import "fmt"
func main() {
var name = "Bob"
var count = 5
fmt.Println(name, count)
}
Execution
go run main.go
Output
Bob 5
  • Type inference makes code cleaner when the type is obvious.
  • Go still enforces strict typing at compile time.

Short declaration operator

Uses the shorthand := operator for quick variable declaration and initialization.

Code
package main
import "fmt"
func main() {
name := "Charlie"
age := 25
city := "New York"
fmt.Println(name, age, city)
}
Execution
go run main.go
Output
Charlie 25 New York
  • ':=' is only available inside functions, not at package level.
  • Cannot use := if variable already exists.

Constants

Defining constants with const keyword, typed/untyped constants, and iota enumeration.

Simple constants

Declares constants in a grouped block using const.

Code
package main
import "fmt"
const (
Pi = 3.14159
MaxRetries = 3
)
func main() {
fmt.Println("Pi:", Pi)
fmt.Println("Max retries:", MaxRetries)
}
Execution
go run main.go
Output
Pi: 3.14159
Max retries: 3
  • Constants must be assigned at compile time.
  • Constants cannot be changed after creation.

Typed constants

Demonstrates typed constants with explicit type specification.

Code
package main
import "fmt"
const (
Status string = "active"
Count int = 10
)
func main() {
fmt.Println(Status, Count)
}
Execution
go run main.go
Output
active 10
  • Typed constants are more restrictive but explicit.
  • Untyped constants are more flexible for operations.

Iota enumeration

Uses iota to create enum-like constants that auto-increment from 0.

Code
package main
import "fmt"
const (
Sunday iota
Monday
Tuesday
Wednesday
)
func main() {
fmt.Println("Monday is:", Monday)
fmt.Println("Wednesday is:", Wednesday)
}
Execution
go run main.go
Output
Monday is: 1
Wednesday is: 3
  • iota starts at 0 and increments for each const line.
  • Perfect for creating enum-like types in Go.

Data Types

Explore Go's type system including basic types, arrays, slices, and pointers.

Basic Types

Strings, integers, floats, booleans, bytes, and other numeric types.

Numeric types

Demonstrates various numeric types including integers, floats, and complex numbers.

Code
package main
import "fmt"
func main() {
var intVal int = 42
var floatVal float64 = 3.14
var complexVal complex128 = 1 + 2i
fmt.Println("Int:", intVal)
fmt.Println("Float:", floatVal)
fmt.Println("Complex:", complexVal)
}
Execution
go run main.go
Output
Int: 42
Float: 3.14
Complex: (1+2i)
  • Use int for most integer operations.
  • float64 is the default floating-point type.
  • complex128 supports complex number operations.

String and character types

Shows string, rune (Unicode), and byte types in Go.

Code
package main
import "fmt"
func main() {
var str string = "Hello, Go!"
var char rune = 'A'
var byteVal byte = 65
fmt.Println("String:", str)
fmt.Println("Rune:", char)
fmt.Println("Byte:", byteVal)
}
Execution
go run main.go
Output
String: Hello, Go!
Rune: 65
Byte: 65
  • Strings are immutable sequences of UTF-8 bytes.
  • rune represents a Unicode code point.
  • byte is an alias for uint8.

Boolean type

Demonstrates the boolean type with logical operations.

Code
package main
import "fmt"
func main() {
var isActive bool = true
var isEmpty bool = false
fmt.Println("Active:", isActive)
fmt.Println("Empty:", isEmpty)
fmt.Println("Not empty:", !isEmpty)
}
Execution
go run main.go
Output
Active: true
Empty: false
Not empty: true
  • bool can only be true or false.
  • Use ! for logical NOT operation.

Arrays and Slices

Fixed-size arrays, dynamic slices, and slice operations.

Array declaration

Shows fixed-size array declaration and initialization.

Code
package main
import "fmt"
func main() {
var arr [3]int = [3]int{1, 2, 3}
arr2 := [...]string{"a", "b", "c"}
fmt.Println("Array 1:", arr)
fmt.Println("Array 2:", arr2)
fmt.Println("Length:", len(arr))
}
Execution
go run main.go
Output
Array 1: [1 2 3]
Array 2: [a b c]
Length: 3
  • Arrays have a fixed size specified at compile time.
  • Use ... to let the compiler infer array size from initialization.

Slice creation and manipulation

Demonstrates dynamic slices with append and slicing operations.

Code
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5}
fmt.Println("Original:", slice)
slice = append(slice, 6)
fmt.Println("After append:", slice)
subSlice := slice[1:4]
fmt.Println("Sub-slice [1:4]:", subSlice)
}
Execution
go run main.go
Output
Original: [1 2 3 4 5]
After append: [1 2 3 4 5 6]
Sub-slice [1:4]: [2 3 4]
  • Slices are dynamic and can grow with append.
  • Slicing is done with [start:end] (end is exclusive).

Make and capacity

Shows how to create slices with specific capacity using make.

Code
package main
import "fmt"
func main() {
slice := make([]int, 0, 10)
fmt.Println("Length:", len(slice), "Capacity:", cap(slice))
for i := 0; i < 5; i++ {
slice = append(slice, i)
}
fmt.Println("After append:", slice)
fmt.Println("New length:", len(slice), "Capacity:", cap(slice))
}
Execution
go run main.go
Output
Length: 0 Capacity: 10
After append: [0 1 2 3 4]
New length: 5 Capacity: 10
  • make creates a slice with length and optional capacity.
  • Capacity is useful for performance optimization to avoid reallocations.

Pointers

Pointer declaration, address operator, and dereferencing.

Pointer basics

Demonstrates pointer declaration, address operator (&), and dereferencing (*).

Code
package main
import "fmt"
func main() {
var x int = 42
var ptr *int = &x
fmt.Println("Value of x:", x)
fmt.Println("Address of x:", &x)
fmt.Println("Pointer ptr:", ptr)
fmt.Println("Dereferenced value:", *ptr)
}
Execution
go run main.go
Output
Value of x: 42
Address of x: 0x...
Pointer ptr: 0x...
Dereferenced value: 42
  • & gives the address of a variable.
  • * dereferences a pointer to get its value.

Pointer modification

Shows how to modify a value through a pointer.

Code
package main
import "fmt"
func main() {
x := 10
ptr := &x
fmt.Println("Before:", x)
*ptr = 20
fmt.Println("After dereference assignment:", x)
}
Execution
go run main.go
Output
Before: 10
After dereference assignment: 20
  • *ptr = value modifies the value that ptr points to.

Nil pointers

Demonstrates nil pointers and nil checking.

Code
package main
import "fmt"
func main() {
var ptr *int
fmt.Println("Nil pointer:", ptr)
fmt.Println("Is nil:", ptr == nil)
x := 5
ptr = &x
fmt.Println("After assignment:", ptr)
fmt.Println("Is nil:", ptr == nil)
}
Execution
go run main.go
Output
Nil pointer: <nil>
Is nil: true
After assignment: 0x...
Is nil: false
  • Uninitialized pointers are nil.
  • Always check if a pointer is nil before dereferencing to avoid panic.

Control Flow

Conditionals, switch statements, and loops in Go.

Conditionals

If/else statements, multiple conditions, and short statements in conditions.

Simple if/else

Basic if/else statement for conditional execution.

Code
package main
import "fmt"
func main() {
x := 10
if x > 5 {
fmt.Println("x is greater than 5")
} else {
fmt.Println("x is not greater than 5")
}
}
Execution
go run main.go
Output
x is greater than 5
  • Braces are required in Go, even for single-statement blocks.

Else if chains

Chains multiple conditions with else if.

Code
package main
import "fmt"
func main() {
score := 85
if score >= 90 {
fmt.Println("Grade: A")
} else if score >= 80 {
fmt.Println("Grade: B")
} else if score >= 70 {
fmt.Println("Grade: C")
} else {
fmt.Println("Grade: F")
}
}
Execution
go run main.go
Output
Grade: B
  • else if is used for multiple conditions in sequence.

Short statement in condition

Declares a variable in the if condition using a short statement.

Code
package main
import "fmt"
func main() {
if x := 10; x > 5 {
fmt.Println("x is greater than 5")
} else if x < 5 {
fmt.Println("x is less than 5")
} else {
fmt.Println("x equals 5")
}
}
Execution
go run main.go
Output
x is greater than 5
  • Variables declared in if condition are scoped to the if/else block.

Switch Statements

Switch/case statements, fallthrough, and type switches.

Basic switch

Basic switch statement with multiple cases and default.

Code
package main
import "fmt"
func main() {
day := 3
switch day {
case 1:
fmt.Println("Monday")
case 2:
fmt.Println("Tuesday")
case 3:
fmt.Println("Wednesday")
default:
fmt.Println("Unknown day")
}
}
Execution
go run main.go
Output
Wednesday
  • No case value matches another; each case is independent.

Switch with fallthrough

Demonstrates fallthrough to execute multiple cases.

Code
package main
import "fmt"
func main() {
fruit := "apple"
switch fruit {
case "apple":
fmt.Println("Red fruit")
fallthrough
case "cherry":
fmt.Println("Small fruit")
case "banana":
fmt.Println("Yellow fruit")
}
}
Execution
go run main.go
Output
Red fruit
Small fruit
  • fallthrough executes the next case's statements.

Type switch

Uses type assertion with switch to handle different types.

Code
package main
import "fmt"
func main() {
var value interface{} = "hello"
switch v := value.(type) {
case string:
fmt.Println("String value:", v)
case int:
fmt.Println("Int value:", v)
case float64:
fmt.Println("Float value:", v)
default:
fmt.Println("Unknown type")
}
}
Execution
go run main.go
Output
String value: hello
  • Type switch uses .(type) to check the underlying type.
  • Useful for working with interface{} values.

Loops

For loops, range iteration, and while-like loops.

Traditional for loop

Demonstrates a traditional for loop with initialization, condition, and increment.

Code
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
fmt.Println("Iteration:", i)
}
}
Execution
go run main.go
Output
Iteration: 0
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
  • Go only has for loops, no while; use for without condition for while behavior.

Range iteration

Uses range to iterate over slices with index and value.

Code
package main
import "fmt"
func main() {
fruits := []string{"apple", "banana", "cherry"}
for idx, fruit := range fruits {
fmt.Println(idx, "-", fruit)
}
}
Execution
go run main.go
Output
0 - apple
1 - banana
2 - cherry
  • range provides both index and value; use _ to ignore either.

Infinite loop with break

Infinite loop using for without condition, exited with break.

Code
package main
import "fmt"
func main() {
count := 0
for {
fmt.Println("Count:", count)
count++
if count >= 3 {
break
}
}
fmt.Println("Done!")
}
Execution
go run main.go
Output
Count: 0
Count: 1
Count: 2
Done!
  • for {} creates an infinite loop; break exits early.

Functions and Methods

Function definition, return types, lambdas, closures, and methods.

Function Definition

Functions with parameters, return types, and multiple returns.

Simple function

Defines a simple function with parameters and a return value.

Code
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func main() {
result := add(5, 3)
fmt.Println("Sum:", result)
}
Execution
go run main.go
Output
Sum: 8
  • Parameters must include their type.
  • Return type comes after parameter list.

Multiple return values

Demonstrates multiple return values, commonly used for error handling.

Code
package main
import "fmt"
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
Execution
go run main.go
Output
Result: 5
  • Multiple returns are wrapped in parentheses.
  • Idiomatic Go returns an error as the last return value.

Named return values

Uses named return values that can be returned implicitly.

Code
package main
import "fmt"
func swap(a, b string) (first string, second string) {
first = b
second = a
return
}
func main() {
x, y := swap("hello", "world")
fmt.Println(x, y)
}
Execution
go run main.go
Output
world hello
  • Named returns instantiate variables; return without args returns them.

Lambdas and Closures

Anonymous functions, function literals, and closures.

Anonymous function

Demonstrates anonymous functions called immediately and assigned to variables.

Code
package main
import "fmt"
func main() {
func(name string) {
fmt.Println("Hello,", name)
}("Go")
greet := func(name string) string {
return "Hi, " + name
}
fmt.Println(greet("Gopher"))
}
Execution
go run main.go
Output
Hello, Go
Hi, Gopher
  • Anonymous functions can be called immediately or assigned.

Closure capturing variables

Closure captures and modifies the outer variable x.

Code
package main
import "fmt"
func main() {
x := 10
increment := func() {
x++
}
increment()
fmt.Println("x after closure:", x)
}
Execution
go run main.go
Output
x after closure 11
  • Closures capture variables by reference, not by value.

Higher-order function

Demonstrates higher-order functions that take functions as parameters.

Code
package main
import "fmt"
func apply(f func(int) int, value int) int {
return f(value)
}
func main() {
square := func(x int) int {
return x * x
}
result := apply(square, 5)
fmt.Println("Square of 5:", result)
}
Execution
go run main.go
Output
Square of 5 25
  • Functions are first-class values in Go.

Methods

Methods with receivers, pointer receivers, and method sets.

Value receiver method

Defines a method with a value receiver; the receiver is a copy.

Code
package main
import "fmt"
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}
func main() {
circle := Circle{Radius: 5}
fmt.Println("Area:", circle.Area())
}
Execution
go run main.go
Output
Area: 78.5
  • Value receiver receives a copy; modifications don't affect original.

Pointer receiver method

Uses pointer receiver to modify the receiver's state.

Code
package main
import "fmt"
type Counter struct {
Count int
}
func (c *Counter) Increment() {
c.Count++
}
func main() {
counter := &Counter{Count: 0}
counter.Increment()
counter.Increment()
fmt.Println("Count:", counter.Count)
}
Execution
go run main.go
Output
Count: 2
  • Pointer receiver allows modification of the receiver.

Multiple methods on same type

Defines multiple methods on the same struct type.

Code
package main
import "fmt"
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)
}
func main() {
rect := Rectangle{Width: 5, Height: 3}
fmt.Println("Area:", rect.Area())
fmt.Println("Perimeter:", rect.Perimeter())
}
Execution
go run main.go
Output
Area: 15
Perimeter: 16
  • Go supports methods on any named type, not just structs.

Packages and Interfaces

Package organization, imports, exporting, and interfaces.

Packages

Package declaration, imports, and import aliases.

Single and grouped imports

Shows grouped import syntax with multiple standard library packages.

Code
package main
import (
"fmt"
"math"
"strings"
)
func main() {
fmt.Println(strings.ToUpper("hello"))
fmt.Println("Pi:", math.Pi)
}
Execution
go run main.go
Output
HELLO
Pi: 3.141592653589793
  • Use parentheses to group multiple imports.
  • Imports are automatically sorted alphabetically.

Import aliases

Uses import aliases to rename package names.

Code
package main
import (
fmt_pkg "fmt"
m "math"
)
func main() {
fmt_pkg.Println("Alias demo")
fmt_pkg.Println("Pi:", m.Pi)
}
Execution
go run main.go
Output
Alias demo
Pi: 3.141592653589793
  • Aliases are useful for avoiding naming conflicts or shortening long names.

Package organization

Shows a package organization example with custom package structure.

Code
myapp/utils/string.go
package utils
import "strings"
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
  • Package name matches directory name (usually).

Exporting

Exported vs unexported identifiers, naming conventions.

Exported function and variable

Shows exported (capitalized) and unexported (lowercase) identifiers.

Code
package math
var MaxValue = 999999
func Add(a, b int) int {
return a + b
}
func private() {
// This function is unexported
}
  • Exported names start with uppercase letters.
  • Unexported names start with lowercase and are only visible within the package.

Exported struct and fields

Struct with exported Name field and unexported age field.

Code
package person
type Person struct {
Name string
age int
}
func NewPerson(name string, age int) *Person {
return &Person{Name: name, age: age}
}
  • Exported struct fields are capitalized.
  • Use constructor functions (NewType) for creating instances.

Interfaces

Interface definition, implicit implementation, type assertions.

Basic interface

Demonstrates interface definition and implicit implementation.

Code
package main
import "fmt"
type Writer interface {
Write(string) error
}
type File struct{}
func (f File) Write(content string) error {
fmt.Println("Writing:", content)
return nil
}
func SaveData(w Writer, data string) {
w.Write(data)
}
func main() {
file := File{}
SaveData(file, "Hello World")
}
Execution
go run main.go
Output
Writing: Hello World
  • Types automatically satisfy interfaces if they implement all methods.

Type assertion

Uses type assertion to extract the underlying type from interface{}.

Code
package main
import "fmt"
func main() {
var val interface{} = "hello"
if str, ok := val.(string); ok {
fmt.Println("String:", str)
} else {
fmt.Println("Not a string")
}
}
Execution
go run main.go
Output
String: hello
  • Type assertion panics if the type is wrong; use ok to safely check.

Empty interface

Uses interface{} to accept values of any type.

Code
package main
import "fmt"
func Print(v interface{}) {
fmt.Println("Value:", v)
}
func main() {
Print(42)
Print("hello")
Print(3.14)
}
Execution
go run main.go
Output
Value: 42
Value: hello
Value: 3.14
  • interface{} is the empty interface that every type implements.

Concurrency

Goroutines, channels, and synchronization patterns.

Goroutines

Creating goroutines with go keyword and concurrent execution.

Simple goroutine

Launches a goroutine with the go keyword for concurrent execution.

Code
package main
import (
"fmt"
"time"
)
func printNumbers() {
for i := 1; i <= 3; i++ {
fmt.Println("Number:", i)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
go printNumbers()
time.Sleep(500 * time.Millisecond)
fmt.Println("Main done")
}
Execution
go run main.go
Output
Number: 1
Number: 2
Number: 3
Main done
  • go launches a goroutine; main must wait for goroutines to complete.

Multiple goroutines

Launches multiple goroutines for concurrent task execution.

Code
package main
import (
"fmt"
"time"
)
func worker(id int) {
for i := 0; i < 2; i++ {
fmt.Printf("Worker %d: task %d\n", id, i)
time.Sleep(50 * time.Millisecond)
}
}
func main() {
for i := 1; i <= 3; i++ {
go worker(i)
}
time.Sleep(200 * time.Millisecond)
}
Execution
go run main.go
Output
Worker 1: task 0
Worker 2: task 0
Worker 3: task 0
Worker 1: task 1
Worker 2: task 1
Worker 3: task 1
  • Goroutines are lightweight; thousands can run concurrently.

Goroutine with closure

Uses closures in goroutines; copies loop variable to avoid race conditions.

Code
package main
import (
"fmt"
"time"
)
func main() {
for i := 1; i <= 3; i++ {
i := i
go func() {
fmt.Println("Goroutine:", i)
}()
}
time.Sleep(100 * time.Millisecond)
}
Execution
go run main.go
Output
Goroutine: 1
Goroutine: 2
Goroutine: 3
  • Copy loop variables in closures: i := i before the goroutine.

Channels

Channel creation, sending/receiving, and channel directions.

Basic channel

Creates a channel and passes data between goroutines.

Code
package main
import "fmt"
func main() {
messages := make(chan string)
go func() {
messages <- "Hello"
}()
msg := <-messages
fmt.Println(msg)
}
Execution
go run main.go
Output
Hello
  • <- is the send/receive operator; direction depends on context.

Receive pattern

Uses a buffered channel to receive multiple values.

Code
package main
import "fmt"
func main() {
results := make(chan string, 2)
go func() {
results <- "Task 1"
results <- "Task 2"
}()
fmt.Println(<-results)
fmt.Println(<-results)
}
Execution
go run main.go
Output
Task 1
Task 2
  • make(chan Type, capacity) creates a buffered channel.

Ranging over channels

Uses range to iterate over channel values until close.

Code
package main
import "fmt"
func main() {
numbers := make(chan int)
go func() {
for i := 1; i <= 3; i++ {
numbers <- i
}
close(numbers)
}()
for num := range numbers {
fmt.Println(num)
}
}
Execution
go run main.go
Output
1
2
3
  • close closes the channel; range exits when channel is closed.

Buffered Channels and Sync

Buffered channels, channel closing, and WaitGroup synchronization.

Buffered channels

Buffered channel with capacity allows sending without immediate receiver.

Code
package main
import "fmt"
func main() {
messages := make(chan string, 2)
messages <- "First"
messages <- "Second"
messages <- "Third"
fmt.Println(<-messages)
fmt.Println(<-messages)
fmt.Println(<-messages)
}
Execution
go run main.go
Output
First
Second
Third
  • Buffered channels have a fixed capacity.
  • Sending blocks only when buffer is full.

Ranging over channel

Iterates over channel until it is closed.

Code
package main
import "fmt"
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
for value := range ch {
fmt.Println(value)
}
}
Execution
go run main.go
Output
1
2
3
  • close signals that no more values will be sent.
  • range exits gracefully when channel is closed.

WaitGroup for synchronization

Uses sync.WaitGroup to wait for all goroutines to complete.

Code
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Println("Worker", id)
}(i)
}
wg.Wait()
fmt.Println("All workers done")
}
Execution
go run main.go
Output
Worker 1
Worker 2
Worker 3
All workers done
  • Add increments counter, Done decrements, Wait blocks until zero.

Advanced Features

Error handling, defer/panic/recover, type conversion, and maps.

Error Handling

Error interface, returning errors, and error checking patterns.

Returning errors

Returns error as second value; check before using the result.

Code
package main
import (
"fmt"
"errors"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)
}
Execution
go run main.go
Output
Result: 5
  • Error is returned as the last value in Go.
  • Check error immediately after the function call.

Custom errors

Implements custom error type with Error method.

Code
package main
import (
"fmt"
"errors"
)
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func main() {
err := ValidationError{"Email", "Invalid format"}
fmt.Println(err)
}
Execution
go run main.go
Output
Email: Invalid format
  • Implement Error() string method to satisfy error interface.

Error wrapping

Wraps errors with context while preserving the original error.

Code
package main
import (
"fmt"
"errors"
)
func main() {
err := errors.New("database error")
wrapped := fmt.Errorf("failed to save user: %w", err)
fmt.Println(wrapped)
if errors.Is(wrapped, err) {
fmt.Println("Found the original error")
}
}
Execution
go run main.go
Output
failed to save user: database error
Found the original error
  • Use %w in fmt.Errorf to wrap errors.
  • Use errors.Is to check for specific errors.

Defer, Panic, and Recover

Defer execution, panic for unrecoverable errors, and recover from panic.

Defer for cleanup

defer ensures code runs when the function exits.

Code
package main
import "fmt"
func main() {
file := "data.txt"
fmt.Println("Opening", file)
defer fmt.Println("Closing", file)
fmt.Println("Processing file")
}
Execution
go run main.go
Output
Opening data.txt
Processing file
Closing data.txt
  • defer executes when the function returns or panics.

Panic usage

Uses panic for errors and recover to handle them.

Code
package main
import "fmt"
func safeDivide(a, b int) int {
if b == 0 {
panic("division by zero")
}
return a / b
}
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from:", r)
}
}()
result := safeDivide(10, 0)
fmt.Println(result)
}
Execution
go run main.go
Output
Recovered from: division by zero
  • panic stops execution; recover returns the panic value in defer.

Multiple defers

Defers execute in LIFO (Last In, First Out) order.

Code
package main
import "fmt"
func main() {
fmt.Println("Start")
defer fmt.Println("First defer")
defer fmt.Println("Second defer")
defer fmt.Println("Third defer")
fmt.Println("End")
}
Execution
go run main.go
Output
Start
End
Third defer
Second defer
First defer
  • Multiple defers form a stack; last defer executes first.

Type Conversion and Maps

Type conversion syntax, maps/dictionaries, and type switching.

Type conversion

Converts between compatible types using Type(value) syntax.

Code
package main
import "fmt"
func main() {
var x int32 = 42
y := int64(x)
z := float64(x)
fmt.Println("Original:", x)
fmt.Println("To int64:", y)
fmt.Println("To float64:", z)
}
Execution
go run main.go
Output
Original: 42
To int64: 42
To float64: 42
  • Type conversion is explicit; implicit conversions are not allowed.

Map creation and access

Creates and manipulates maps with key-value pairs.

Code
package main
import "fmt"
func main() {
scores := map[string]int{
"Alice": 90,
"Bob": 85,
"Charlie": 92,
}
fmt.Println("Alice's score:", scores["Alice"])
scores["David"] = 88
fmt.Println("David's score:", scores["David"])
if val, ok := scores["Eve"]; ok {
fmt.Println("Eve's score:", val)
} else {
fmt.Println("Eve not found")
}
}
Execution
go run main.go
Output
Alice's score: 90
David's score: 88
Eve not found
  • Maps are unordered; use two-value receive to check existence.

Iterating over maps

Iterates over map keys and values using range.

Code
package main
import "fmt"
func main() {
colors := map[string]string{
"red": "#FF0000",
"green": "#00FF00",
"blue": "#0000FF",
}
for name, hex := range colors {
fmt.Printf("%s: %s\n", name, hex)
}
}
Execution
go run main.go
Output
red: #FF0000
green: #00FF00
blue: #0000FF
  • Maps are iterated in random order; use ordering if needed.