Skip to main content
Master Go’s fundamental data structures through comprehensive exercises covering arrays, slices, maps, and structs.

Arrays

Learn to work with fixed-size arrays and understand their fundamentals.

Level I - Array Basics

Objective: Practice array declaration syntaxDeclare arrays of different types without initialization.Key Concepts:
  • Array declaration syntax
  • Fixed size requirement
  • Zero values
Approach:
var numbers [5]int
var names [3]string
var prices [10]float64
Objective: Access and modify array elementsPractice indexing arrays to read and write values.Key Concepts:
  • Zero-based indexing
  • Array element access
  • Bounds checking
Approach:
var arr [3]int
arr[0] = 10
arr[1] = 20
arr[2] = 30
fmt.Println(arr[1])  // 20
Objective: Initialize arrays with valuesRefactor array declarations to use literal syntax.Approach:
// Instead of:
var arr [3]int
arr[0] = 1
arr[1] = 2
arr[2] = 3

// Use literal:
arr := [3]int{1, 2, 3}
Objective: Let the compiler count array sizeUse ... to automatically determine array length.Approach:
arr := [...]int{1, 2, 3, 4, 5}
// Compiler determines size is 5
Objective: Debug common array mistakesFix programs with array-related errors.Common Issues:
  • Index out of bounds
  • Type mismatches
  • Size mismatches
Approach: Read error messages carefully and check array sizes and types.
Objective: Learn array comparison rulesCompare arrays using == and != operators.Key Concepts:
  • Arrays are comparable if same type and size
  • Element-wise comparison
  • Array equality
Approach:
arr1 := [3]int{1, 2, 3}
arr2 := [3]int{1, 2, 3}
fmt.Println(arr1 == arr2)  // true
Objective: Copy arrays through assignmentUnderstand array assignment behavior.Key Concepts:
  • Arrays are value types
  • Assignment creates a copy
  • Changes to copy don’t affect original
Approach:
arr1 := [3]int{1, 2, 3}
arr2 := arr1  // Copy
arr2[0] = 10  // arr1 unchanged

Level II - Array Applications

Objective: Print array elements with formattingCreate a program that formats and prints array data.Approach: Loop through an array and print each element with custom formatting.
Objective: Use arrays for currency conversionStore exchange rates in an array and perform conversions.Key Concepts:
  • Parallel arrays
  • Array-based lookups
  • Calculations with arrays
Approach:
  1. Create array of currency codes
  2. Create array of exchange rates
  3. Find index and calculate conversion
Objective: Calculate array statisticsCompute the average of numbers in an array.Approach:
sum := 0
for _, num := range numbers {
    sum += num
}
average := float64(sum) / float64(len(numbers))
Objective: Sort array elementsImplement a sorting algorithm for arrays.Approach:
  • Bubble sort
  • Selection sort
  • Or use the sort package
Objective: Search for words in an arrayFind specific words in an array of strings.Approach: Loop through array and compare strings.

Slices

Master Go’s dynamic slices, the most commonly used sequence type.

Level I - Slice Basics

Objective: Understand nil slicesDeclare slices without initialization.Key Concepts:
  • Nil slice vs empty slice
  • Zero value for slices
  • Checking for nil
Approach:
var numbers []int
fmt.Println(numbers == nil)  // true
Objective: Create empty but non-nil slicesInitialize slices with zero length but non-nil.Approach:
numbers := []int{}
// or
numbers := make([]int, 0)
Objective: Initialize slices with valuesCreate slices using literal syntax.Approach:
numbers := []int{1, 2, 3, 4, 5}
names := []string{"Alice", "Bob", "Charlie"}
Objective: Convert array declarations to slicesRefactor array code to use slices instead.Approach: Remove the size specification from array declarations.
Objective: Debug slice-related errorsFix common mistakes when working with slices.Common Issues:
  • Nil pointer dereferences
  • Index out of range
  • Type mismatches
Objective: Learn slice comparison limitationsUnderstand why slices can’t be compared with ==.Key Concepts:
  • Slices are not comparable
  • Use loops or reflect.DeepEqual
  • Byte slice exception with bytes.Equal
Approach:
// Error: cannot compare
// slice1 == slice2

// Correct:
equal := reflect.DeepEqual(slice1, slice2)

Level II - Appending

Objective: Use append function with byte slicesPractice appending elements and comparing byte slices.Key Concepts:
  • append() function
  • Variadic appending
  • bytes.Equal() for comparison
Approach:
slice := []byte{1, 2, 3}
slice = append(slice, 4, 5)
Objective: Understand append behavior with nilAppend to nil slices and observe behavior.Approach:
var slice []int
slice = append(slice, 1, 2, 3)
// Works fine! Creates new slice
Objective: Debug append mistakesFix programs with incorrect append usage.Common Mistakes:
  • Not assigning append result
  • Incorrect variadic syntax
  • Type mismatches
Objective: Build and sort dynamic listsAppend numbers from input and sort them.Approach:
numbers := []int{}
for _, arg := range os.Args[1:] {
    num, _ := strconv.Atoi(arg)
    numbers = append(numbers, num)
}
sort.Ints(numbers)
Objective: Manage dynamic price dataStore and process housing prices using slices.Approach:
  1. Read prices into a slice
  2. Append new prices
  3. Calculate statistics
Objective: Enhance with statistical analysisAdd average, min, and max calculations.Approach: Calculate statistics as you append new prices.

Level III - Slicing Operations

Objective: Extract sub-slicesPractice slice expressions to get portions of slices.Key Concepts:
  • Slice syntax [start:end]
  • Omitting start or end
  • Full slice expression
Approach:
numbers := []int{0, 1, 2, 3, 4, 5}
fmt.Println(numbers[1:4])   // [1 2 3]
fmt.Println(numbers[:3])    // [0 1 2]
fmt.Println(numbers[3:])    // [3 4 5]
Objective: Dynamic slicing with user inputUse command-line arguments to control slicing.Approach: Parse start and end indices from arguments and slice accordingly.
Objective: Extract price rangesSlice housing price data to analyze specific ranges.Approach: Use slicing to get recent prices, price ranges, etc.

Level IV - Slice Internals

Objective: Understand shared backing arraysFix problems caused by slices sharing backing arrays.Key Concepts:
  • Backing array concept
  • Slice header (pointer, length, capacity)
  • Unintended modifications
Approach: Use copy() or full slice expression to avoid sharing.
Objective: Observe backing array effectsSee how sorting affects related slices.Approach: Create multiple slices from one array, sort one, observe others.
Objective: Monitor slice memory behaviorObserve when new backing arrays are allocated.Approach: Print capacity and address of backing array during appends.
Objective: Understand len vs capObserve how length and capacity change.Approach:
slice := make([]int, 3, 5)
fmt.Println(len(slice))  // 3
fmt.Println(cap(slice))  // 5
Objective: Watch capacity allocation strategyObserve how Go grows slice capacity.Approach: Append to a slice and print capacity after each append.

Level V - Advanced Operations

Objective: Master copy, append, and slicingWarm up with various slice manipulation exercises.Operations:
  • Copying slices
  • Inserting elements
  • Deleting elements
  • Concatenating slices
Objective: Control slice exposurePrevent external code from modifying your slice’s backing array.Approach: Use full slice expression [start:end:max] to limit capacity.
Objective: Prevent backing array retentionFix memory leaks caused by retaining large backing arrays.Approach: Use copy() to create independent slices when keeping small portions of large slices.
Objective: Complex slice manipulationInsert newlines after sentences using copy().Approach: Create a new buffer and copy with insertions.

Maps

Learn Go’s built-in hash table type for key-value storage.
Objective: Create and print mapsDeclare and initialize maps of different types.Key Concepts:
  • Map declaration
  • Map initialization
  • Nil maps vs empty maps
Approach:
// Nil map
var m map[string]int

// Empty map
m := map[string]int{}

// With make
m := make(map[string]int)
Objective: Add and retrieve map valuesAdd elements and look them up by key.Key Concepts:
  • Adding key-value pairs
  • Retrieving values
  • Comma-ok idiom
Approach:
m := make(map[string]int)
m["age"] = 25
m["score"] = 100

age, exists := m["age"]
if exists {
    fmt.Println(age)
}
Objective: Group data with mapsCreate a map of Hogwarts houses to student names.Key Concepts:
  • Map of slices
  • Grouping data
  • Map iteration
Approach:
houses := map[string][]string{
    "Gryffindor": {"Harry", "Ron", "Hermione"},
    "Slytherin":  {"Draco", "Crabbe", "Goyle"},
}

students := houses["Gryffindor"]

Structs

Create custom types to organize related data.
Objective: Load data into a game storeDefine structs and initialize them with game data.Key Concepts:
  • Struct definition
  • Struct literals
  • Field access
Approach:
type Game struct {
    ID    int
    Name  string
    Price float64
}

game := Game{
    ID:    1,
    Name:  "Tetris",
    Price: 9.99,
}
Objective: Build an interactive interfaceAdd a command-line interface to list games using bufio.Scanner.Key Concepts:
  • User input with Scanner
  • Slice of structs
  • Formatting output
Approach:
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
    cmd := scanner.Text()
    if cmd == "list" {
        // Print all games
    }
}
Objective: Search for specific gamesAdd ID-based game lookup functionality.Approach:
  1. Parse ID from input
  2. Search through game slice
  3. Display matching game
Objective: Serialize struct dataConvert game data to JSON format.Key Concepts:
  • encoding/json package
  • json.Marshal()
  • Struct tags
Approach:
type Game struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Price float64 `json:"price"`
}

data, err := json.Marshal(games)
Objective: Load data from JSONParse JSON data into struct instances.Approach:
var games []Game
err := json.Unmarshal(data, &games)

Next Steps

Functions

Learn to organize code with functions

Getting Started

Review the basics

Build docs developers (and LLMs) love