In this article, we will explore type assertion and type conversion operations in Go using some examples.
If you would like to run the snippets as you follow along, you’ll need:
Here are the major topics regarding type-casting in Go that you will learn about during the course of this article:
Type assertion (as the name implies) is used to assert the type of a given variable. In Go, this is done by checking the underlying type of an empty interface variable.
Type conversion is the process of changing a variable from one type to another specified type. For example, we can convert an int value to a float64.
In Go, the syntax for type assertions is t := i.(type)
. Here is a snippet of a full type assertion operation:
// type-lessons.go package main func main() { var i interface{} = "a string" t := i.(string) // "a string" }
The type assertion operation consists of three main elements:
i
, which is the variable whose type we are asserting. This variable must be defined as an interfacetype
, which is the type we are asserting our variable is (such as string, int, float64, etc)t
, which is a variable that stores the value of our variable i
, if our type assertion is correctIt should be further noted that when type assertions are unsuccessful, it throws an error referred to as a “panic” in Go.
From the above we can see that for a type assertion to be successful, we need to specify the correct type to begin with, but in several cases we might not be sure of the type and would like to prevent panics being thrown unnoticed in our scripts.
We can handle the uncertainty in type assertions in two ways, which we’ll look at now.
The first option is by using a second variable on the left side of our type assertions. The type assertion can return two values; the first value (like t
above, which is the value of the variable we are checking); and the second, which is a boolean indicator of whether the assertion succeeded.
// type-lessons.go ... var i interface{} = "a string" t, ok := i.(string)
Our second variable, ok
, is a boolean value that holds whether our type assertion was correct or not. So, in our example above, ok
would be true
because i
is of type string
.
However, this method is only useful for validating an intuition of a variable’s type without a panic being thrown. If we are unsure of the type of a variable, we can use our second option, which is called Type switching.
This is similar to a normal switch statement, which switches through possible types of a variable, rather than the normal switching of values. Here, we will extract the type from our interface variable and switch through several type options.
// type-lessons.go package main func main() { var testVar interface{} = 42.56 switch t := testVar.(type) { case string: fmt.Println("Variable is of type string!") case int: fmt.Println("Variable is of type int!") case float32: fmt.Println("Variable is of type float32!") default: fmt.Println("Variable type unknown") } }
As we mentioned earlier, type assertions in Go can only be performed on interfaces. You might ask, what is an interface, what is an empty interface, and how can it have a type? Well, let’s take a look.
An interface is a definition of the methods and attributes of a variable type. An empty interface is one where the attributes, methods, and the type are not specified until it is initialized.
For example:
var test interface{} = "help!"
Our test
variable above is defined as an empty interface, but by assignment takes the type string
. Type assertions are performed on interface variables to assert their underlying type.
We’ve talked about empty interfaces and how they get their type at initialization, but you may also be wondering what exactly type is.
Types in Go define how the variable is stored and what methods are available to the variable. In Go, there are basic types such as string, rune, int, and bool, which other types can be built on.
We’ve talked about what type is, now let’s talk about type conversion.
Type conversion is simply changing a value from one type to another, but in Go there is a caveat, which is that types can only be converted between related or similar types. Let’s look at an example:
// type-conversion.go package main import ( "fmt" "strings" ) type myString string func (m myString) capitalize() myString { capStr := strings.ToUpper(string(m)) return myString(capStr) } func main() { fmt.Println("Hello World!") var m myString = "test" fmt.Println(m.capitalize()) }
In our example above, we have defined a type myString
using the basic type string
. This means that myString
inherits the data structure of string
, but we can give it its own methods that type string
will not have, like the method capitalize
in our example.
We can also see in our example that we were able to convert our type myString
to string
and convert string
to myString
. This is because they share similar data structures.
Another example of types that can be converted explicitly (without using any special tricks or libraries) is int
to float64
and vice versa. Here’s a simple example:
// type-conversion-int.go package main import "fmt" func main() { var simpleInt int = 3 var simpleFloat float64 = 4.5 // fmt.Println("This will throw an error: ", simpleInt + simpleFloat) fmt.Println("This will work correctly: ", simpleInt + int(simpleFloat)) fmt.Println("This will work correctly too: ", float64(simpleInt) + simpleFloat) }
Line 12 of our snippet will fail if attempted because Go does not allow numeric operations on mismatched types.
In our example above, we were able to convert between int
and float64
explicitly, just as we could between myString
and string
, because they both have similar data structures — but we cannot convert string
to int
.
A very notable difference between type assertion and type conversion is the syntax of each — type assertion has the syntax variable.(type)
, while type conversion has the syntax type(variable)
.
However, beyond this we see that type assertions rely on an interface variable that accepts a type by value assignment so as to extract the underlying type, while type conversions rely on the similarity of the data structures of the types for them to be convertible.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowDing! You got a notification, but does it cause a little bump of dopamine or a slow drag of cortisol? […]
A guide for using JWT authentication to prevent basic security issues while understanding the shortcomings of JWTs.
Auth.js makes adding authentication to web apps easier and more secure. Let’s discuss why you should use it in your projects.
Compare Auth.js and Lucia Auth for Next.js authentication, exploring their features, session management differences, and design paradigms.