Skip to Content
Course content

23: Empty Interface and Type Assertions

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

What exactly is an empty interface anyway?

You'll see interface{} everywhere in older Go code, and in newer versions, you'll see the alias any. They are the exact same thing. Think of an empty interface as a container that can hold literally any value—an integer, a string, a custom struct, or even another interface. Why? Because an interface defines a set of methods a type must have. Since the empty interface has zero methods, every single type in Go satisfies it by default.

I usually tell people to think of it as the "escape hatch" of Go's type system. I've used this a lot when writing functions that need to handle data where the type isn't known until runtime, like parsing a JSON response from an API where a field could be a number or a string.

package main

import "fmt"

func describe(i any) {
    fmt.Printf("Value: %v, Type: %T\n", i, i)
}

func main() {
    describe(42)            // Works!
    describe("Hello Go")    // Works!
    describe(true)          // Works!
}

How do I get the concrete type back out?

Holding a value in an any container is easy, but it's also a bit useless because you can't do anything type-specific with it. You can't add two any variables together, even if you know they are both integers. To get the original value back, you use a type assertion.

The syntax is value.(Type). It's basically you telling the compiler, "Trust me, I know this empty interface is actually a string."

func printLength(i any) {
    // We assert that 'i' is a string so we can use len()
    s := i.(string) 
    fmt.Println("Length is:", len(s))
}

What happens if I guess the type wrong?

If you use the single-value assertion I showed above and you're wrong—say, you pass an integer into printLength—your program will panic and crash immediately. In a production environment, that's a nightmare. I almost never use the single-value assertion unless I'm 100% certain of the type (which is rarer than you'd think).

Instead, use the "comma-ok" idiom. This gives you a second boolean value that tells you if the assertion succeeded without blowing up your app.

func safePrintLength(i any) {
    s, ok := i.(string)
    if !ok {
        fmt.Println("Oops, this wasn't a string!")
        return
    }
    fmt.Println("Length is:", len(s))
}

Can I handle multiple different types in one block?

If you find yourself writing a long chain of if/else statements with comma-ok assertions, you're doing it the hard way. Go provides a type switch, which is a much cleaner way to handle a variety of possible types. It's essentially a switch statement specifically designed for type assertions.

I use this all the time when building flexible data processors. Here's how it looks in practice:

func processValue(i any) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Processing an integer: %d\n", v * 2)
    case string:
        fmt.Printf("Processing a string: %s\n", v)
    case bool:
        fmt.Printf("Processing a boolean: %t\n", v)
    default:
        fmt.Println("Unknown type!")
    }
}



📋 Practical Task

Build a Heterogeneous Data Summarizer

Your task is to create a program that can process a slice of mixed data types and produce a summary. Imagine you are receiving a stream of mixed logs containing both IDs (integers) and Messages (strings).

  • Create a slice of any (or interface{}) containing at least five elements: a mix of strings and integers.
  • Write a function called summarize that takes this slice as an argument.
  • Inside summarize, use a type switch to:
    • Sum up all the integers found in the slice.
    • Concatenate all the strings into one long sentence (separated by spaces).
  • The function should print the final total sum and the final concatenated string.
  • Ensure that any types that are neither int nor string are simply ignored.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.