Type Assertion in Golang

Earlier, we discussed interfaces in Golang and how they can be used in a program. We use the variable of the interface type to call the methods implemented by the type.

Below is a sample example of where we have used an interface in a program.

package main

import "fmt"

// "Shape" interface
type Shape interface {
	area() float64
}

// "Circle" type
type Circle struct {
	radius float64
}

func (c Circle) area() float64 {
	pie := 3.14
	return (pie) * (c.radius) * (c.radius)
}

func main() {
	var shape Shape // making a variable

	// pointing the variable to "Circle" type
	shape = Circle{
		radius: 2,
	}

	fmt.Println("Area of circle: ", shape.area())

}

Output –

Area of circle:  12.56

Here we have used the variable of an interface type and called the method implementations provided by the concrete type.

Now, what if we want our concrete type back? Can we directly do the type conversion as we do with all types?

package main

import "fmt"

// "Shape" interface
type Shape interface {
	area() float64
}

// "Circle" type
type Circle struct {
	radius float64
}

func (c Circle) area() float64 {
	pie := 3.14
	return (pie) * (c.radius) * (c.radius)
}

func main() {
	var shape Shape // making a variable

	// pointing the variable to "Circle" type
	shape = Circle{
		radius: 2,
	}

	circle := Circle(shape)

	fmt.Println("Radius of circle:", circle.radius)

}

Output –

 cannot convert shape (type Shape) to type Circle: need type assertion

On line 28, we tried to get the concrete type back from the interface type using a type conversion but got the below error.

cannot convert shape (type Shape) to type Circle: need type assertion

This error tells us to use the type assertion.

What is type assertion? A type assertion lets us get the concrete type back.

We have our “Shape” interface and a “Circle” type, and we will try to get the “Circle” type from the interface type variable. We can use the below syntax –

var shape Shape  // making a variable

// pointing the variable to "Circle" type
shape = Circle{
	radius: 2,
}

circle := shape.(Circle)  // using type assertion to get concrete type back
Type assertion

Now, let’s use it in a program.

package main

import "fmt"

// "Shape" interface
type Shape interface {
	area() float64
}

// "Circle" type
type Circle struct {
	radius float64
}

func (c Circle) area() float64 {
	pie := 3.14
	return (pie) * (c.radius) * (c.radius)
}

func main() {
	var shape Shape // making a variable

	// pointing the variable to "Circle" type
	shape = Circle{
		radius: 2,
	}

	circle := shape.(Circle)

	fmt.Println("Radius of circle:", circle.radius)

}

Output –

Radius of circle: 2
Failure during type assertion

Let’s create one more type, “Rectangle“, that implements the “Shape” interface.

// "Rectangle" type
type Rectangle struct {
    length  float64
    breadth float64
}
 
func (r Rectangle) area() float64 {
    return r.length * r.breadth
}

Now, we will include one more method perimeter() in the “Circle” type, which is not declared in the “Shape” interface.

func (c Circle) perimeter() float64 {
	pie := 3.14
	return 2 * (pie) * (c.radius)
}

We will calculate the circle’s perimeter using the findPerimeter() function.

package main

import "fmt"

// "Shape" interface
type Shape interface {
	area() float64
}

// "Circle" type
type Circle struct {
	radius float64
}

func (c Circle) area() float64 {
	pie := 3.14
	return (pie) * (c.radius) * (c.radius)
}

func (c Circle) perimeter() float64 {
	pie := 3.14
	return 2 * (pie) * (c.radius)
}

// "Rectangle" type
type Rectangle struct {
	length  float64
	breadth float64
}

func (r Rectangle) area() float64 {
	return r.length * r.breadth
}

func findPerimeter(shape Shape) {
	circle := shape.(Circle)
	// Calling a method which is not declared in interface
	fmt.Println("Perimeter of circle:", circle.perimeter())
}

func main() {

	findPerimeter(Circle{radius: 2})

}

Output –

Perimeter of circle: 12.56

Let’s pass the Rectangle instead of the Circle type as the findPerimeter() function argument.

package main

import "fmt"

// "Shape" interface
type Shape interface {
	area() float64
}

// "Circle" type
type Circle struct {
	radius float64
}

func (c Circle) area() float64 {
	pie := 3.14
	return (pie) * (c.radius) * (c.radius)
}

func (c Circle) perimeter() float64 {
	pie := 3.14
	return 2 * (pie) * (c.radius)
}

// "Rectangle" type
type Rectangle struct {
	length  float64
	breadth float64
}

func (r Rectangle) area() float64 {
	return r.length * r.breadth
}

func findPerimeter(shape Shape) {
	circle := shape.(Circle)
	// Calling a method which is not declared in interface
	fmt.Println("Perimeter of circle:", circle.perimeter())
}

func main() {

	findPerimeter(Rectangle{length: 2, breadth: 3})

}

Output –

panic: interface conversion: main.Shape is main.Rectangle, not main.Circle

Everything looked fine, but Golang threw a runtime panic.

How to avoid panic when type assertion fails

If a type assertion is used in a context that expects only one return value, and the original type doesn’t match the type in the assertion, the program will panic at runtime (not when compiling). To avoid this, we can use a second optional return value indicating whether the assertion is successful. And the assertion won’t panic if it’s unsuccessful.

Usually, the second variable is named “ok“. Its value will be true if the type assertion is successful; Otherwise false.

circle, ok := shape.(Circle)
package main

import "fmt"

// "Shape" interface
type Shape interface {
	area() float64
}

// "Circle" type
type Circle struct {
	radius float64
}

func (c Circle) area() float64 {
	pie := 3.14
	return (pie) * (c.radius) * (c.radius)
}

func (c Circle) perimeter() float64 {
	pie := 3.14
	return 2 * (pie) * (c.radius)
}

// "Rectangle" type
type Rectangle struct {
	length  float64
	breadth float64
}

func (r Rectangle) area() float64 {
	return r.length * r.breadth
}

func findPerimeter(shape Shape) {
	circle, ok := shape.(Circle)

	if ok {
		fmt.Println("Perimeter of circle:", circle.perimeter())
	} else {
		fmt.Println("Type assertion failed")
	}

}

func main() {

	findPerimeter(Rectangle{length: 2, breadth: 3})

}

Output –

Type assertion failed

This is how we can avoid panic during type assertions.

We hope you have liked the article. If you have any doubts or concerns, please feel free to write us in the comments or mail us at [email protected]

Related Articles –
Liked the article? Share this on

Leave a Comment

Your email address will not be published.