Go Empty Interface

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 {}Code language: Go (go)

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"})
}Code language: Go (go)

Output:

Hi
100
true
[1 2 3 4 5]
[apple banana strawberry]Code language: Go (go)

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...)
}Code language: Go (go)

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)
}Code language: Go (go)

Output:

print("Hi", 10)Code language: Go (go)

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...)
}Code language: Go (go)

Here’s the definition of the type any in the Go’s fmt package:

type any = interface{}Code language: Go (go)

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)
    }
}Code language: Go (go)

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{}Code language: Go (go)

Second, declare and initialize a slice that can store a mix of values of any type:

s := AnySlice{1, "one", 2.0, true}Code language: Go (go)

Third, iterate over the elements of the slice and display them on the screen:

for _, v := range s {
    fmt.Println(v)
}Code language: Go (go)

Output:

1
one
2
trueCode language: Go (go)

Handling 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)
}Code language: Go (go)

Output:

map[age:25 name:Anthony tags:[golang programming]]Code language: Go (go)

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")
    }
}Code language: Go (go)

How it works.

First, store a literal string in a variable of type interface {}:

var x interface{} = "hello"Code language: Go (go)

Second, assert that the type of x is interface {}:

msg, ok := x.(string)Code language: Go (go)

The x.(string) attempts to assert that the dynamic value stored in the variable x is of type string. It returns two values:

  • msg holds the asserted value if the assertion is successful.
  • ok is true if the assertion is successful and false otherwise.

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")
}Code language: Go (go)

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")
        }
    }
}Code language: Go (go)

Output:

string: Apple
int: 100
Unknown type
bool: trueCode language: Go (go)

Summary

  • 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.
Was this tutorial helpful?