Golang DSA Chapter 2 Part 2: Slices, Maps, and Go Patterns

Welcome back. In Part 1 we covered arrays, basic slices, two-dimensional slices, and maps. That was the foundation. Now Kommadi moves into the more interesting Go patterns: variadic functions, defer and panic, and a full CRUD web application that ties it all together. He also shows more advanced slice operations along the way.

Let me break down what matters in this second half of Chapter 2.

Variadic Functions

A variadic function is one that accepts any number of arguments of the same type. You’ve probably used one already without thinking about it: fmt.Println is variadic. You can throw as many arguments at it as you want.

The syntax uses three dots (...) before the type of the last parameter. That tells Go “this parameter can take zero or more values.”

Here is the thing, Kommadi doesn’t show a standalone variadic function example. Instead he demonstrates the concept through fmt.Println calls in his database code:

fmt.Println("Before Insert", customers)

Println accepts a ...any as its parameter. You can pass it one thing, five things, whatever. Go packs them into a slice behind the scenes.

If you want to write your own variadic function, it looks like this:

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

// call it with any number of ints
result := sum(1, 2, 3, 4, 5)

The nums parameter inside the function is just a regular []int slice. The variadic syntax is purely a convenience for the caller. You can also pass an existing slice to a variadic function by adding ... after it:

numbers := []int{1, 2, 3}
result := sum(numbers...)

That’s the whole concept. Nothing complicated. But it’s one of those Go features you’ll use constantly once you know it exists.

Database Operations: Where Patterns Come Together

This is where Kommadi spends most of the chapter’s second half. He builds a Customer database layer using MySQL, and honestly it’s a good way to see several Go patterns working together in real code.

The setup is a Customer struct and a GetConnection function:

type Customer struct {
    CustomerId   int
    CustomerName string
    SSN          string
}

func GetConnection() (database *sql.DB) {
    databaseDriver := "mysql"
    databaseUser := "newuser"
    databasePass := "newuser"
    databaseName := "crm"
    database, error := sql.Open(databaseDriver,
        databaseUser+":"+databasePass+"@/"+databaseName)
    if error != nil {
        panic(error.Error())
    }
    return database
}

Notice the named return value (database *sql.DB) in the function signature. That’s a Go convention you’ll see a lot. The variable database is declared in the signature and returned implicitly.

Reading Data with GetCustomers

The GetCustomers method queries the database and builds a slice of customers:

func GetCustomers() []Customer {
    var database *sql.DB
    database = GetConnection()

    var rows *sql.Rows
    rows, error := database.Query(
        "SELECT * FROM Customer ORDER BY Customerid DESC")
    if error != nil {
        panic(error.Error())
    }

    var customers []Customer
    customers = []Customer{}
    for rows.Next() {
        var customerId int
        var customerName string
        var ssn string
        error = rows.Scan(&customerId, &customerName, &ssn)
        if error != nil {
            panic(error.Error())
        }
        customer := Customer{
            CustomerId:   customerId,
            CustomerName: customerName,
            SSN:          ssn,
        }
        customers = append(customers, customer)
    }

    defer database.Close()
    return customers
}

There are two important things happening here. First, append is used to grow the customers slice dynamically. Second, defer database.Close() is sitting right before the return. Let me explain both.

Advanced Slice Operations: append in Practice

In Part 1 we saw basic append usage. Here it shows up in a real scenario: building a collection of unknown size by appending to it in a loop.

customers = append(customers, customer)

Every time the loop finds a new row, it appends a Customer to the slice. If the slice runs out of capacity, Go automatically allocates a bigger underlying array, copies the old data over, and returns the new slice. You don’t have to manage any of that yourself.

Kommadi also shows sub-slicing in his earlier examples. A quick recap:

var arr = []int{5, 6, 7, 8, 9}
var slic1 = arr[:3]     // [5, 6, 7]
var slic2 = arr[1:5]    // [6, 7, 8, 9]
var slic3 = append(slic2, 12)  // [6, 7, 8, 9, 12]

The syntax arr[start:end] gives you a new slice that references the same underlying array. The start index is inclusive, the end is exclusive. If you skip start, it defaults to 0. If you skip end, it goes to the last element.

One gotcha: since sub-slices share the same underlying array, modifying one can affect the other. Keep that in mind when working with sub-slices.

The defer Statement

defer is one of Go’s most useful keywords. It delays the execution of a function call until the surrounding function returns. No matter how the function exits, whether normally or through a panic, deferred calls always run.

In the database code, Kommadi uses it like this:

defer database.Close()

This ensures the database connection gets closed when the function returns. Without defer, you’d have to remember to call database.Close() before every return statement. With multiple return paths (error cases, early returns), that gets messy fast. defer lets you put the cleanup right next to the resource creation and forget about it.

Deferred calls execute in LIFO order (last in, first out). So if you defer three things, the third one runs first when the function exits.

The book uses defer in every database method: GetCustomers, InsertCustomer, UpdateCustomer, DeleteCustomer. Same pattern every time. Open connection, do work, defer the close.

The panic Function

You’ll notice Kommadi uses panic a lot in his error handling:

if error != nil {
    panic(error.Error())
}

panic stops the normal flow of the current goroutine immediately. It’s like throwing an exception in other languages, but more dramatic. When a function panics, it stops executing, runs any deferred functions, and then returns to the caller, which also panics, and so on up the stack until the program crashes.

Here’s the important bit: deferred functions still run after a panic. That’s why defer database.Close() works even when the code panics. The connection still gets cleaned up.

In production code, you’d want to use proper error handling with returned error values instead of panicking everywhere. But for learning purposes, the book uses panic to keep the examples short and focused on the data structure concepts.

recover: Catching Panics

Go provides a recover function that can stop a panic from crashing your program. You use it inside a deferred function:

func safeOperation() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()

    panic("something went wrong")
}

When recover is called inside a deferred function during a panic, it captures the panic value and returns it. The program continues normally from after the deferred call. If there’s no panic, recover returns nil.

The pattern of defer + recover is how Go handles exceptional situations without try-catch blocks. It’s simpler and more explicit, which fits Go’s overall philosophy.

CRUD Web Forms: Putting It All Together

The biggest example in this half of the chapter is a complete CRM web application. Kommadi builds it using Go’s standard net/http package and text/template for HTML rendering.

The web server setup is straightforward:

func main() {
    log.Println("Server started on: http://localhost:8000")
    http.HandleFunc("/", Home)
    http.HandleFunc("/alter", Alter)
    http.HandleFunc("/create", Create)
    http.HandleFunc("/update", Update)
    http.HandleFunc("/view", View)
    http.HandleFunc("/insert", Insert)
    http.HandleFunc("/delete", Delete)
    http.ListenAndServe(":8000", nil)
}

Each route maps to a handler function. The handlers use the database operations we just covered to read and write customer data, then render HTML templates.

For example, the Insert handler reads form values, creates a customer, and re-renders the home page:

func Insert(writer http.ResponseWriter, request *http.Request) {
    var customer Customer
    customer.CustomerName = request.FormValue("customername")
    customer.SSN = request.FormValue("ssn")
    InsertCustomer(customer)

    var customers []Customer
    customers = GetCustomers()
    template_html.ExecuteTemplate(writer, "Home", customers)
}

The templates use Go’s template syntax with {{ }} delimiters and compose together using {{ template "Header" }} and {{ template "Footer" }}. Nothing fancy, but it shows how Go handles server-side rendering.

What I find interesting here is that the same Go patterns keep showing up: slices for holding collections of customers, append for building those collections, defer for closing database connections, panic for error handling. The web app is just a wrapper around the data structure operations.

What to Take Away

This second half of Chapter 2 is really about Go patterns more than data structures. Here’s what matters:

Variadic functions let you pass any number of arguments. The three dots syntax ... is your friend. You’ll use it with fmt.Println, append, and your own functions.

defer is essential for cleanup. Put it right after you acquire a resource, and Go guarantees it runs when the function exits. Use it for closing files, database connections, unlocking mutexes.

panic and recover are Go’s exception mechanism. Panic crashes the program (after running deferred calls). Recover catches panics inside deferred functions. In real code, prefer returning errors over panicking.

Slice operations like append and sub-slicing are the backbone of dynamic collections in Go. The slice will grow automatically when you append past its capacity. Sub-slices share the underlying array.

The CRM example ties everything together into a working web app. It’s verbose, but it shows how these patterns work in practice with actual database I/O.

Next up, Kommadi moves into linear data structures: lists, sets, and tuples. That’s where the DSA content really starts.


Previous: Chapter 2 Part 1: Go Basics for Data Structures

Next: Chapter 3 Part 1: Lists, Sets, and Tuples

About

About BookGrill.net

BookGrill.net is a technology book review site for developers, engineers, and anyone who builds things with code. We cover books on software engineering, AI and machine learning, cybersecurity, systems design, and the culture of technology.

Know More