Type assertions in Go

Michał Łowicki
Sep 7, 2016 · 3 min read

Type assertions are used to check if value held by interface type variable either implements desired interface or is of a concrete type.

Syntax of type assertion is defined as:

PrimaryExpression.(Type)

PrimaryExpression can be found in language specification but it could an identifier, element of an array at particular index, slice etc.

Type is either a type identifier or type literal like:

type I interface {
walk()
quack()
}
type S struct{}func (s S) walk() {}
func (s S) quack() {}
var i I
i = S{}
fmt.Println(i.(interface {
walk()
}))

PrimaryExpression must always evaluate to interface type otherwise it’s a compile-time error:

type I interface {
walk()
quack()
}
type S struct{}
S{}.(I) // invalid type assertion: S literal.(I) (non-interface type S on left)

If expression is nil then type assertion never holds.

Dynamic type

Besides static type that all variables have (it’s a type from variable’s declaration), variables of interface type also have a dynamic type. It’s a type of value currently set in interface type variable. Over the course of program execution variable of interface type has the same static type but its dynamic type can change as different values implementing desired interface will be assigned:

type I interface {
walk()
}
type A struct{}func (a A) walk() {}type B struct{}func (b B) walk() {}func main() {
var i I
i = A{} // dynamic type of i is A
fmt.Printf(“%T\n”, i.(A))
i = B{} // dynamic type of i is B
fmt.Printf(“%T\n”, i.(B))
}

Interface type

If T from v.(T) is an interface type then such assertion checks if dynamic type of v implements interface T:

type I interface {
walk()
}
type J interface {
quack()
}
type K interface {
bark()
}
type S struct{}func (s S) walk() {}func (s S) quack() {}func main() {
var i I
i = S{}
fmt.Printf(“%T\n”, i.(J))
fmt.Printf(“%T\n”, i.(K)) // panic: interface conversion: main.S is not main.K: missing method bark
}

Not interface type

If T from v.(T) is not an interface type then such assertion checks if dynamic type of v is identical to T:

type I interface {
walk()
}
type A struct{}func (a A) walk() {}type B struct{}func (b B) walk() {}func main() {
var i I
i = A{}
fmt.Printf(“%T\n”, i.(A))
fmt.Printf(“%T\n”, i.(B)) // panic: interface conversion: main.I is main.A, not main.B
}

Type passed in non-interface type case must implement interface I as not fulfilling this requirement will be caught while compilation:

type C struct{}
fmt.Printf(“%T\n”, i.(C))

outputs:

impossible type assertion:
C does not implement I (missing walk method)

Don’t panic!

In all above cases when assertion doesn’t hold run-time panic will be triggered. To handle failures gracefully there are special forms of assignments or initializations:

type I interface {
walk()
}
type A struct {
name string
}
func (a A) walk() {}type B struct {
name string
}
func (b B) walk() {}func main() {
var i I
i = A{name: “foo”}
valA, okA := i.(A)
fmt.Printf("%#v %#v\n", valA, okA)
valB, okB := i.(B)
fmt.Printf("%#v %#v\n", valB, okB)
}

and it gives:

main.A{name:"foo"} true
main.B{name:””} false

When assertion doesn’t hold then first value will be zero value for tested type.

Resources:

golangspec

A series dedicated to deeply understand Go’s specification and language’s nuances

Michał Łowicki

Written by

Software engineering manager at Facebook, previously Opera, never satisfied.

golangspec

A series dedicated to deeply understand Go’s specification and language’s nuances

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade