Go Lesson Plan
A progressive curriculum to master Go through hands-on practice.
Lesson 1: First Steps
Section titled “Lesson 1: First Steps”Goal: Run Go and understand basic types.
Concepts
Section titled “Concepts”Go is statically typed, compiled, and emphasizes simplicity. Every Go file
belongs to a package. main is the entry point.
Exercises
Section titled “Exercises”-
Create and run a program
main.go package mainimport "fmt"func main() {fmt.Println("Hello, World!")}Terminal window go run main.gogo build main.go && ./main -
Variables and types
// Explicit typevar name string = "Go"var age int = 10// Type inferencecount := 42pi := 3.14// Constantsconst MaxSize = 100 -
Basic types
// Numbersvar i int = 42var f float64 = 3.14var c complex128 = 1 + 2i// Stringss := "hello"len(s) // 5s[0] // 104 (byte value of 'h')s + " world" // "hello world"// Booleansvar flag bool = true -
Zero values
var i int // 0var f float64 // 0.0var s string // ""var b bool // falsevar p *int // nil
Checkpoint
Section titled “Checkpoint”Write a program that declares variables of different types and prints them.
Lesson 2: Control Flow
Section titled “Lesson 2: Control Flow”Goal: Use conditionals and loops.
Concepts
Section titled “Concepts”Go has only one loop construct: for. No parentheses around conditions. Braces
are required.
Exercises
Section titled “Exercises”-
If statements
x := 10if x > 0 {fmt.Println("positive")} else if x < 0 {fmt.Println("negative")} else {fmt.Println("zero")}// With initializationif err := doSomething(); err != nil {fmt.Println(err)} -
For loops
// Traditionalfor i := 0; i < 5; i++ {fmt.Println(i)}// While-stylen := 0for n < 5 {fmt.Println(n)n++}// Infinitefor {break // Exit}// Rangenums := []int{1, 2, 3}for i, v := range nums {fmt.Printf("%d: %d", i, v)} -
Switch
day := "Monday"switch day {case "Monday":fmt.Println("Start of week")case "Friday":fmt.Println("Almost weekend")default:fmt.Println("Regular day")}// No condition (like if-else chain)switch {case x < 0:fmt.Println("negative")case x > 0:fmt.Println("positive")default:fmt.Println("zero")} -
Defer
func main() {defer fmt.Println("cleanup") // Runs at function exitfmt.Println("working")}// Output: working, cleanup
Checkpoint
Section titled “Checkpoint”Write a FizzBuzz program using a for loop and switch.
Lesson 3: Collections
Section titled “Lesson 3: Collections”Goal: Work with arrays, slices, and maps.
Exercises
Section titled “Exercises”-
Arrays (fixed size)
var arr [3]int // [0, 0, 0]arr[0] = 1len(arr) // 3arr2 := [3]int{1, 2, 3}arr3 := [...]int{1, 2, 3} // Size inferred -
Slices (dynamic)
// Creates := []int{1, 2, 3}s2 := make([]int, 3) // [0, 0, 0]s3 := make([]int, 0, 10) // len=0, cap=10// Appends = append(s, 4, 5)// Slice operationss[1:3] // [2, 3]s[:2] // [1, 2]s[2:] // [3, 4, 5]// Copydst := make([]int, len(s))copy(dst, s) -
Maps
// Createm := map[string]int{"alice": 30,"bob": 25,}m2 := make(map[string]int)// Accessage := m["alice"]age, ok := m["unknown"] // ok = false if missing// Modifym["charlie"] = 35delete(m, "bob")// Iteratefor key, value := range m {fmt.Printf("%s: %d", key, value)} -
Nil slices and maps
var s []int // nil, len=0, can appendvar m map[string]int // nil, cannot write (panic)m = make(map[string]int) // Now safe to write
Checkpoint
Section titled “Checkpoint”Create a word frequency counter using a map.
Lesson 4: Functions
Section titled “Lesson 4: Functions”Goal: Define functions and understand multiple returns.
Exercises
Section titled “Exercises”-
Basic functions
func add(a int, b int) int {return a + b}// Same-type parametersfunc add2(a, b int) int {return a + b} -
Multiple return values
func divide(a, b float64) (float64, error) {if b == 0 {return 0, errors.New("division by zero")}return a / b, nil}result, err := divide(10, 2)if err != nil {log.Fatal(err)} -
Named returns
func split(sum int) (x, y int) {x = sum * 4 / 9y = sum - xreturn // Returns x and y} -
Variadic and closures
// Variadicfunc sum(nums ...int) int {total := 0for _, n := range nums {total += n}return total}sum(1, 2, 3, 4) // 10// Closurefunc counter() func() int {count := 0return func() int {count++return count}}
Checkpoint
Section titled “Checkpoint”Write a function that returns both the min and max of a slice.
Lesson 5: Structs and Methods
Section titled “Lesson 5: Structs and Methods”Goal: Define types with methods.
Exercises
Section titled “Exercises”-
Structs
type Person struct {Name stringAge int}// Createp1 := Person{Name: "Alice", Age: 30}p2 := Person{"Bob", 25} // Positionalp3 := new(Person) // *Person, zero values// Accessp1.Name = "Alicia" -
Methods
type Rectangle struct {Width, Height float64}// Value receiver (copy)func (r Rectangle) Area() float64 {return r.Width * r.Height}// Pointer receiver (mutate)func (r *Rectangle) Scale(factor float64) {r.Width *= factorr.Height *= factor}rect := Rectangle{10, 5}rect.Area() // 50rect.Scale(2) // Now 20x10 -
Embedding
type Animal struct {Name string}func (a Animal) Speak() string {return "..."}type Dog struct {Animal // EmbeddedBreed string}d := Dog{Animal{"Rex"}, "Labrador"}d.Name // "Rex" (promoted field)d.Speak() // "..." (promoted method) -
Struct tags
type User struct {Name string `json:"name"`Email string `json:"email,omitempty"`}
Checkpoint
Section titled “Checkpoint”Create a Stack struct with Push and Pop methods.
Lesson 6: Interfaces
Section titled “Lesson 6: Interfaces”Goal: Define behavior with interfaces.
Concepts
Section titled “Concepts”Interfaces are satisfied implicitly. A type implements an interface by implementing its methods.
Exercises
Section titled “Exercises”-
Define and implement
type Speaker interface {Speak() string}type Dog struct{ Name string }func (d Dog) Speak() string {return "Woof!"}var s Speaker = Dog{"Rex"}s.Speak() // "Woof!" -
Empty interface
func printAny(v interface{}) {fmt.Println(v)}// Or with any (Go 1.18+)func printAny2(v any) {fmt.Println(v)} -
Type assertions
var i interface{} = "hello"s := i.(string) // Panics if wrong types, ok := i.(string) // ok = false if wrong// Type switchswitch v := i.(type) {case string:fmt.Println("string:", v)case int:fmt.Println("int:", v)default:fmt.Println("unknown")} -
Common interfaces
io.Reader type Reader interface {Read(p []byte) (n int, err error)}// io.Writertype Writer interface {Write(p []byte) (n int, err error)}// errortype error interface {Error() string}
Checkpoint
Section titled “Checkpoint”Define a Shape interface with Area() method. Implement for Circle and
Rectangle.
Lesson 7: Concurrency
Section titled “Lesson 7: Concurrency”Goal: Use goroutines and channels.
Exercises
Section titled “Exercises”-
Goroutines
func say(s string) {for i := 0; i < 3; i++ {fmt.Println(s)time.Sleep(100 * time.Millisecond)}}go say("hello") // Runs concurrentlysay("world") // Runs in main -
Channels
ch := make(chan int)go func() {ch <- 42 // Send}()value := <-ch // Receive (blocks)fmt.Println(value)// Buffered channelch2 := make(chan int, 3) -
Select
ch1 := make(chan string)ch2 := make(chan string)go func() { ch1 <- "one" }()go func() { ch2 <- "two" }()select {case msg := <-ch1:fmt.Println(msg)case msg := <-ch2:fmt.Println(msg)case <-time.After(1 * time.Second):fmt.Println("timeout")} -
WaitGroup
var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)go func(n int) {defer wg.Done()fmt.Println(n)}(i)}wg.Wait() // Block until all done
Checkpoint
Section titled “Checkpoint”Create a worker pool with 3 workers processing jobs from a channel.
Lesson 8: Error Handling and Testing
Section titled “Lesson 8: Error Handling and Testing”Goal: Handle errors idiomatically and write tests.
Exercises
Section titled “Exercises”-
Error handling
func readFile(path string) ([]byte, error) {data, err := os.ReadFile(path)if err != nil {return nil, fmt.Errorf("reading %s: %w", path, err)}return data, nil}// Check errordata, err := readFile("config.json")if err != nil {log.Fatal(err)} -
Custom errors
type ValidationError struct {Field stringMessage string}func (e ValidationError) Error() string {return fmt.Sprintf("%s: %s", e.Field, e.Message)}// errors.Is and errors.Asif errors.Is(err, os.ErrNotExist) {// Handle missing file}var valErr ValidationErrorif errors.As(err, &valErr) {fmt.Println(valErr.Field)} -
Writing tests
add_test.go package mainimport "testing"func TestAdd(t *testing.T) {got := add(2, 3)want := 5if got != want {t.Errorf("add(2, 3) = %d; want %d", got, want)}}// Run: go test -
Table-driven tests
func TestAdd(t *testing.T) {tests := []struct {a, b, want int}{{1, 2, 3},{0, 0, 0},{-1, 1, 0},}for _, tt := range tests {t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {got := add(tt.a, tt.b)if got != tt.want {t.Errorf("got %d, want %d", got, tt.want)}})}}
Checkpoint
Section titled “Checkpoint”Write a function with custom error type and table-driven tests.
Practice Projects
Section titled “Practice Projects”Project 1: CLI Tool
Section titled “Project 1: CLI Tool”Build a command-line file utility:
- Use flag package for arguments
- Read/write files
- Handle errors gracefully
Project 2: HTTP Server
Section titled “Project 2: HTTP Server”Build a REST API:
- Use net/http
- JSON encoding/decoding
- Middleware pattern
Project 3: Concurrent Fetcher
Section titled “Project 3: Concurrent Fetcher”Fetch multiple URLs concurrently:
- Use goroutines and channels
- Implement timeout
- Aggregate results
Quick Reference
Section titled “Quick Reference”| Stage | Topics |
|---|---|
| Beginner | Types, control flow, functions, collections |
| Intermediate | Structs, methods, interfaces, error handling |
| Advanced | Goroutines, channels, testing, reflection |
See Also
Section titled “See Also”- Testing — Go test patterns
- Problem Solving — Debugging techniques
- Concurrency