Skip to Content
Course content

60: Generics in Go: Type Parameters

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Wait, why do I need type parameters? Can't I just use "any"?

I get this question a lot. If you've been using any (which is just an alias for interface{}), you know the pain: you lose your type safety. Every time you pull a value out of an any slice or map, you have to use a type assertion like val.(int). It's tedious, and it's a runtime crash waiting to happen if you guess the type wrong.

Type parameters let us write code that is generic but remains statically typed. Instead of saying "this function takes any type," we say "this function takes a type T, and whatever T happens to be when the function is called, it will be consistent throughout the entire operation."

// Without generics, we'd need a version for int, one for float64, etc.
// With type parameters, we define [T int | float64]
func Max[T int | float64](a, b T) T {
    if a > b {
        return a
    }
    return b
}

func main() {
    fmt.Println(Max(10, 20))             // T is inferred as int
    fmt.Println(Max(3.14, 2.71))         // T is inferred as float64
}

Notice how I didn't have to cast anything? The compiler knows that if I pass in two floats, I'm getting a float back.

How do I restrict which types can be used as parameters?

You can't just use any random type for every generic function. For example, in the Max function above, I used >. You can't use > on a struct or a map. That's where constraints come in.

A constraint is just an interface that defines the set of permissible types. While I used a simple union (int | float64) above, for real-world projects, I usually define a named interface to keep things clean. I also frequently use the built-in comparable constraint, which tells Go that the type must support == and !=.

type Number interface {
    int | int64 | float64
}

// This function only accepts types that satisfy the Number interface
func Sum[T Number](slice []T) T {
    var total T
    for _, v := range slice {
        total += v
    }
    return total
}

If you try to pass a slice of strings into Sum, the code won't even compile. I prefer this over a runtime panic any day of the week.

Can I use these in structs, or just functions?

Absolutely. This is where generics really start to shine, especially when you're building data structures. Think about a Response wrapper for an API. You always have the same metadata (status, message), but the Data field changes depending on the endpoint.

Before generics, you'd make Data any. Now, you can make the entire struct generic.

type APIResponse[T any] struct {
    Status  int
    Message string
    Data    T
}

type User struct {
    Name string
    Age  int
}

func main() {
    // A response specifically for a User
    userResp := APIResponse[User]{
        Status:  200,
        Message: "Success",
        Data:    User{Name: "Alice", Age: 30},
    }
    
    // A response specifically for a list of strings
    tagsResp := APIResponse[[]string]{
        Status:  200,
        Message: "Success",
        Data:    []string{"golang", "generics", "coding"},
    }
}

I love this pattern because it documents exactly what the response should contain without forcing the caller to perform unsafe type assertions every time they access the Data field.




📋 Practical Task

Build a Generic UniqueSet

In Go, we often use a map[T]bool or map[T]struct{} to simulate a "Set" (a collection of unique elements). Your task is to wrap this logic into a generic data structure so it can be reused for any comparable type.

Requirements:

  • Create a generic struct called UniqueSet[T comparable] that holds a map internally.
  • Implement a method Add(value T) that adds an element to the set.
  • Implement a method Contains(value T) bool that checks if an element exists.
  • Implement a method Count() int that returns the number of unique elements.

Test your implementation by creating one set for int and another set for string to ensure the type parameters are working correctly.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.