Golang - Interfaces - The Empty Interface and any
Introduction
The empty interface is an interface that has zero methods. It is written as interface{}.
Since every concrete type has zero or more methods, every type satisfies the empty interface. This means you can store a value of any type in a variable of type interface{}.
In Go 1.18 and later, any was introduced as an alias for interface{}. You should prefer using any in modern Go code as it is more readable and concise.
Analogy: An any (interface{}) variable is like a box that can hold anything. You can put an integer, a string, a struct, or any other value into it. The box doesn’t know or care what’s inside.
Why Use any?
The primary use case for any is to write functions that can accept arguments of any type. The most famous example is fmt.Println:
// The signature is effectively:
func Println(a ...any) (n int, err error)
This allows fmt.Println to accept any number and combination of types.
package main
import "fmt"
func main() {
var a any // `any` is an alias for `interface{}`
a = 42
fmt.Println(a) // 42
a = "hello"
fmt.Println(a) // hello
a = struct{ Name string }{"Alice"}
fmt.Println(a) // {Alice}
}
The Problem with any: Loss of Type Safety
While any is flexible, it comes at a cost: you lose static type safety. When you put a value into an any variable, the compiler “forgets” its original type. You can’t perform type-specific operations on it directly.
var a any = "hello"
// This will not compile!
// The compiler only knows the type is `any`, not `string`.
// It doesn't know if `any` has a `len()` function.
// fmt.Println(len(a)) // Error: invalid argument: a (variable of type any) has no len
To use the value, you must get its underlying concrete type back. This is done with a type assertion.
Getting the Type Back: Type Assertions
A type assertion is an operation that checks the underlying type of an interface variable and extracts it.
value, ok := a.(T)
a: The variable of typeany(or another interface).T: The concrete type you think is inside.value: If the assertion is correct,valuewill hold the value with typeT.ok: A boolean that istrueif the assertion succeeded andfalseotherwise.
This “comma, ok” idiom is the safe way to perform a type assertion, as it prevents a panic if the type is not what you expected.
package main
import "fmt"
func describe(a any) {
// Safely assert the type to string
s, ok := a.(string)
if ok {
fmt.Printf("It's a string: '%s'\n", s)
return
}
// Safely assert the type to int
i, ok := a.(int)
if ok {
fmt.Printf("It's an int: %d\n", i)
return
}
fmt.Println("It's some other type.")
}
func main() {
describe("hello") // It's a string: 'hello'
describe(123) // It's an int: 123
describe(3.14) // It's some other type.
}
When to Use any (and When Not To)
Use any when:
- You are writing truly generic code that needs to operate on values of unknown types (e.g., encoding/json, fmt.Println, database drivers).
- You need a collection of mixed types (though this is often a sign that a better data structure could be used).
Avoid any when:
- You can use a more specific interface type. If you only need a
Writemethod, accept anio.Writer, notany. - You can use Go Generics (Go 1.18+). Generics provide a way to write type-safe functions and data structures that work with a set of types, without the runtime overhead and loss of type safety associated with
any.
Interview Question: “What is an empty interface or any? When should you use it?”
Answer: “any is an interface with no methods, so all types satisfy it. It’s used to hold a value of any type. However, you lose static type safety and must use a type assertion to get the original type back. It’s useful for functions like fmt.Println, but for your own code, you should prefer specific interfaces or, in modern Go, generics, as they are more type-safe.”