Golang Type Assertion Explained with Examples


GO

Author: Tuan Nguyen
Reviewer: Deepak Prasad

Getting started with golang Type Assertion

A type assertion is an operation applied to an interface value. Syntactically, it looks like x.(T), where x is an expression of an interface type and T is a type, called the “asserted” type. A type assertion checks that the dynamic type of its operand matches the asserted type.

There are two possibilities. First, if the asserted type T is a concrete type, then the type assertion checks whether x’s dynamic type is identical to T. If this check succeeds, the result of the type assertion is x’s dynamic value, whose type is of course T. In other words, a type assertion to a concrete type extracts the concrete value from its operand. If the check fails, then the operation panics. For example:

var w io.Writer
w = os.Stdout
f := w.(*os.File)      // success: f == os.Stdout
c := w.(*bytes.Buffer) // panic: interface holds *os.File, not *bytes.Buffer

Second, if instead the asserted type T is an interface type, then the type assertion checks whether x’s dynamic type satisfies T. If this check succeeds, the dynamic value is not extracted; the result is still an interface value with the same type and value components, but the result has the interface type T.

In other words, a type assertion to an interface type changes the type of the expression, making a different (and usually larger) set of methods accessible, but it preserves the dynamic type and value components inside the interface value.

After the first type assertion below, both w and rw hold os.Stdout so each has a dynamic type of *os.File, but w, an io.Writer, exposes only the file’s Write method, whereas rw exposes its Read method too.

var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) // success: *os.File has both Read and Write

w = new(ByteCounter)
rw = w.(io.ReadWriter) // panic: *ByteCounter has no Read method

No matter what type was asserted, if the operand is a nil interface value, the type assertion fails. A type assertion to a less restrictive interface type (one with fewer methods) is rarely needed, as it behaves just like an assignment, except in the nil case.

w = rw             // io.ReadWriter is assignable to io.Writer
w = rw.(io.Writer) // fails only if rw == nil

 

Example 1: Simple example of using type assertion in Golang

The code shown below using type assertion to check the concrete type of an interface:

package main

import (
	"fmt"
)

// main function
func main() {

	// an interface which has a string value
	var checkInterface interface{} = "GoLinuxCloud"

	// assigning value of interface type to checkType variable
	checkType := checkInterface.(string)

	// printing the concrete value
	fmt.Println(checkType)

	// panic because interface does not have int type
	checkTypeInt := checkInterface.(int)

	fmt.Println(checkTypeInt)
}

Output:

GoLinuxCloud
panic: interface conversion: interface {} is string, not int        

goroutine 1 [running]:
main.main()
        C:/Users/H518272/Desktop/golang/main/milestone41.go:20 +0x78
exit status 2

Explanation:

  • The variable checkInterface is interface{} type which means it can hold any type.
  • The concrete type that is assigned to the variable is string
  • If the type assertion is against the int type, running this program will produce a run-time error because we have wrong type assertion.

 

Example 2: Check Type Assertion status using ok comma idiom

The below example use ok comma idiom to check assertion is successful or not:

package main
  
import (
    "fmt"
)
  

func main() {
      
    // an interface has a floaf64 value
    var checkInterface interface{} = 11.04
    
	// assigning value of interface type to checkType variable
    checkType := checkInterface.(float64)
      
    // printing the concrete value
    fmt.Println(checkType)
      
    // test whether interface has string type and
    // return true if found or
    // false otherwise
    checkType2, ok := checkInterface.(string)
    if ok {
        fmt.Println("Correct assertion!")
        fmt.Println(checkType2)
    } else {
      
        fmt.Println("Wrong assertion!")
    }
}

Output:

11.04
Wrong assertion!

 

Example 3: Logging type assertion errors

We can modify the code in example 3 a little bit to log more information when type assertion has errors. One way is we use the printf format string %T that produces the type.

    checkType2, ok := checkInterface.(string)
    if ok {
        fmt.Println("Correct assertion!")
        fmt.Println(checkType2)
    } else {
        fmt.Printf("Wrong assertion, got data of type %T but wanted String!", checkInterface)
    }

 

Example 4: Using Type Switch to determine type of interface

What happens when you do not know the data type before attempting a type assertion? How can you differentiate between the supported data types and the unsupported ones? How can you choose a different action for each supported data type?

The answer is by using type switches. Type switches use switch blocks for data types and allow you to differentiate between type assertion values, which are data types, and process each data type the way you want. On the other hand, in order to use the empty interface in type switches, you need to use type assertions.

In this example, we will use Switch key word to determine interface's type.

package main
  
import (
    "fmt"
)
  
func main() {
	var testInterface interface{} = "GolinuxCLoud"
	var testInterface2 interface{} = map[string]int{"Anna": 4, "Bob": 10, "Clair": 11}
  
	switch testInterface.(type) {
	  case string: 
		fmt.Println(testInterface, "is a string!")
	  case int:
		fmt.Println(testInterface, "is an int!")
	  case float64:
		fmt.Println(testInterface, "is a float64!")
	  default:
		fmt.Println(testInterface, "is not basic type!")
	}

	switch testInterface2.(type) {
		case string: 
		  fmt.Println(testInterface2, "is a string!")
		case int:
		  fmt.Println(testInterface2, "is an int!")
		case float64:
		  fmt.Println(testInterface2, "is a float64!")
		default:
		  fmt.Println(testInterface2, "is not basic type!")
	  }
  }

Output:

GolinuxCLoud is a string!
map[Anna:4 Bob:10 Clair:11] is not basic type!

Explanation:

  • Create 2 interface: one contains a string and one contains a map
  • Use the .(type) function and Switch key word to compare the type of underlying interface's data

 

Summary

type assertion is a mechanism for working with the underlying concrete value of an interface. This mainly happens because interfaces are virtual data types without their own values—interfaces just define behavior and do not hold data of their own.

Type assertions use the x.(T) notation, where x is an interface type and T is a type, and help you extract the value that is hidden behind the empty interface. For a type assertion to work, x should not be nil and the dynamic type of x should be identical to the T type.

 

References

https://pkg.go.dev/fmt

 

Tuan Nguyen

Tuan Nguyen

He is proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise spanning these technologies, he develops robust solutions and implements efficient data processing and management strategies across various projects and platforms. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment