Summary: in this tutorial, you will learn about the Go empty interface (interface {}) that can hold a value of any type.
Go interface {}
In Go, you can declare an interface without any methods:
interface {}It is called an empty interface.
The empty interface is the most general type in Go’s type system because you can use it to represent any type.
Empty interfaces are flexible because you can use them in functions or data structures that can work with values of any type.
Defining generic functions
In the following program, we define a print() function that uses a parameter with the type is an empty interface:
package main
import "fmt"
func print(x interface{}) {
fmt.Println(x)
}
func main() {
print("Hi")
print(100)
print(true)
print([]int{1, 2, 3, 4, 5})
print([]string{"apple", "banana", "strawberry"})
}Output:
Hi
100
true
[1 2 3 4 5]
[apple banana strawberry]The print() function accepts any type and uses the Println function from the fmt package to write the value to the standard output, which is the screen.
Defining variadic functions with empty interfaces
To allow the print() function to accept a variable number of arguments, you can convert it to a variadic function as follows:
func print(args ...interface{}) {
fmt.Println(args...)
}In this syntax, the ..., which appears in front of the interface {}, denotes that the print() function can accept a variable number of arguments. Since the type of each argument is an interface {}, you can pass arguments of any type to the print() function.
The following program how to pass any number of arguments of any type to the print() function:
package main
import "fmt"
func print(args ...interface{}) {
fmt.Println(args...)
}
func main() {
print("Hi", 10)
}Output:
print("Hi", 10)Notice that the fmt.Println() function uses the type any, which is the alias of an empty interface (interface {}):
func Println(a ...any) (n int, err error) {
return Fprintln(os.Stdout, a...)
}Here’s the definition of the type any in the Go’s fmt package:
type any = interface{}Defining data structures of any types
You can use empty interfaces to define a data structure such as arrays or slices. For example:
package main
import "fmt"
type AnySlice []interface{}
func main() {
s := AnySlice{1, "one", 2.0, true}
for _, v := range s {
fmt.Println(v)
}
}How it works.
First, define an alias for a slice of empty interface {}, which is a slice of values of any type:
type AnySlice []interface{}Second, declare and initialize a slice that can store a mix of values of any type:
s := AnySlice{1, "one", 2.0, true}Third, iterate over the elements of the slice and display them on the screen:
for _, v := range s {
fmt.Println(v)
}Output:
1
one
2
trueHandling JSON with dynamic data
When working with JSON or other data formats where you don’t know the structure at compile time, you can use the interface {} to represent the data. For example:
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
profileJSON := `{"name":"Anthony", "age":25, "tags":["golang", "programming"]}`
var data map[string]interface{}
if err := json.Unmarshal([]byte(profileJSON), &data); err != nil {
fmt.Print(err)
os.Exit(1)
}
fmt.Println(data)
}Output:
map[age:25 name:Anthony tags:[golang programming]]Type Assertions
In Go, type assertions allow you to retrieve the dynamic value stored in the interface {} variable and convert it back to its original type. For example:
package main
import (
"fmt"
"strings"
)
func main() {
// store the value in an empty interface
var x interface{} = "hello"
// type assertion
msg, ok := x.(string)
// if x is a string
if ok {
fmt.Println(strings.ToLower(msg))
} else {
fmt.Println("x is not a string")
}
}How it works.
First, store a literal string in a variable of type interface {}:
var x interface{} = "hello"Second, assert that the type of x is interface {}:
msg, ok := x.(string)The x.(string) attempts to assert that the dynamic value stored in the variable x is of type string. It returns two values:
msgholds the asserted value if the assertion is successful.okistrueif the assertion is successful andfalseotherwise.
Third, check the value of ok, if it is true, meaning the value stored in the x variable is a string:
if ok {
fmt.Println(strings.ToLower(msg))
} else {
fmt.Println("x is not a string")
}Type Switch
In Go, you can use a type switch to process values of different types stored in an interface {} variable. For example:
package main
import "fmt"
func main() {
values := []interface{}{"Apple", 100, 15.5, true}
for _, value := range values {
switch v := value.(type) {
case int:
fmt.Println("int:", v)
case string:
fmt.Println("string:", v)
case bool:
fmt.Println("bool:", v)
default:
fmt.Println("Unknown type")
}
}
}Output:
string: Apple
int: 100
Unknown type
bool: trueSummary
- An empty interface is an interface without method signatures.
- An empty interface
interface{}can represent a value of any type. - Use empty interfaces to create generic functions and data structures that handle dynamic data.
- Use type assertions to retrieve the original values stored in an empty interface.
- Use type switches to process values of different types stored in an empty interface.