DEV Community

Cover image for A Comprehensive Guide to Type Casting and Conversions in Go
zakaria chahboun
zakaria chahboun

Posted on • Updated on

A Comprehensive Guide to Type Casting and Conversions in Go

Go, also known as Golang, is a statically typed language. This means that the type of every variable is known at compile time, providing safety and predictability in your code. However, this also requires that any conversion from one type to another is explicit and deliberate. In this article, we’ll explore the various type casting and conversion mechanisms available in Go, from basic numeric conversions to more complex interface and pointer conversions.

1. Basic Type Conversions

Go allows conversion between basic types like integers, floating-point numbers, and strings, but these conversions must be done explicitly.

Numeric Types

Conversions between different numeric types are straightforward but must be explicit:

var i int = 42
var f float64 = float64(i)  // int to float64
var u uint = uint(i)        // int to uint
Enter fullscreen mode Exit fullscreen mode

In this example, we convert an int to a float64 and to a uint. These conversions are explicit because Go does not perform automatic (implicit) type conversions.

String and Byte Slice

Go strings are immutable, but they can be converted to and from byte slices ([]byte), which are mutable:

var s string = "hello"
var b []byte = []byte(s)   // string to []byte
var s2 string = string(b)  // []byte to string
Enter fullscreen mode Exit fullscreen mode

Similarly, you can convert between strings and rune slices ([]rune), where rune is a type alias for int32:

var r []rune = []rune(s)   // string to []rune
var s3 string = string(r)  // []rune to string
Enter fullscreen mode Exit fullscreen mode

2. Custom Type Conversions

In Go, you can define your own types based on existing ones. Conversions between custom types and their underlying types are explicit:

type MyInt int
var i int = 10
var mi MyInt = MyInt(i)   // int to MyInt
var i2 int = int(mi)      // MyInt to int
Enter fullscreen mode Exit fullscreen mode

This explicit conversion is necessary to ensure that the compiler can verify the safety of your code.

3. Pointer Conversions

Pointers in Go reference the memory address of a variable. You can convert between a value and its pointer:

var x int = 42
var p *int = &x     // int to *int (pointer to int)
var y int = *p      // *int to int (dereferencing)
Enter fullscreen mode Exit fullscreen mode

4. Interface Type Conversions

Interfaces in Go are used to define a set of methods. You can convert between concrete types and interfaces:

var a interface{} = 42    // int to interface{}
var b int = a.(int)       // interface{} to int (type assertion)
Enter fullscreen mode Exit fullscreen mode

Type Assertions

A type assertion provides access to an interface's concrete value:

if v, ok := a.(int); ok {
    fmt.Println("a is an int:", v)
}
Enter fullscreen mode Exit fullscreen mode

Type Switch

A type switch allows you to perform different actions based on the dynamic type of an interface:

switch v := a.(type) {
case int:
    fmt.Println("a is an int:", v)
case string:
    fmt.Println("a is a string:", v)
default:
    fmt.Println("a is of unknown type")
}
Enter fullscreen mode Exit fullscreen mode

5. Unsafe Conversions

The unsafe package allows you to bypass Go’s type safety, enabling conversions that would otherwise be illegal:

import "unsafe"

var i int = 42
var p *int = &i
var fp *float64 = (*float64)(unsafe.Pointer(p))  // *int to *float64
Enter fullscreen mode Exit fullscreen mode

Warning: Unsafe conversions should be used sparingly and only when absolutely necessary, as they can lead to undefined behavior.

6. Channel Type Conversions

Channels are a powerful feature in Go, allowing communication between goroutines. You can convert between bidirectional and unidirectional channels:

ch := make(chan int)
var sendOnlyChan chan<- int = ch  // bidirectional to send-only
var recvOnlyChan <-chan int = ch  // bidirectional to receive-only
Enter fullscreen mode Exit fullscreen mode

7. Struct and Array Conversions

Conversions between structs or arrays with identical layouts require explicit casting:

type Point struct {
    X, Y int
}

type Coord struct {
    X, Y int
}

var p Point = Point{1, 2}
var c Coord = Coord(p)  // Convert Point to Coord (same field types)
Enter fullscreen mode Exit fullscreen mode

8. Slice Conversions

Slices are references to arrays, and while you can convert between slices of the same type, converting between different types of slices requires an explicit conversion:

var a []int = []int{1, 2, 3}
var b []int = a[1:]  // Convert a slice to another slice of the same type
Enter fullscreen mode Exit fullscreen mode

9. Nil Interface Conversions

A nil value in Go can be assigned to any interface type:

var x interface{} = nil
var y error = nil
Enter fullscreen mode Exit fullscreen mode

10. Function Type Conversions

Go functions can be converted to different types, provided the signatures are compatible:

type FuncType func(int) int

func square(x int) int {
    return x * x
}

var f FuncType = FuncType(square)  // Convert function to FuncType
Enter fullscreen mode Exit fullscreen mode

11. Array-to-Slice Conversion

You can create a slice from an array, which is essentially a reference to the array:

var arr [5]int = [5]int{1, 2, 3, 4, 5}
var sl []int = arr[:]  // Convert array to slice
Enter fullscreen mode Exit fullscreen mode

Conclusion

Type casting and conversions in Go are explicit by design, making the code safer and easier to understand. By requiring explicit conversions, Go helps prevent subtle bugs that can arise from implicit type coercion, common in some other programming languages. Understanding these conversions and using them correctly is crucial for writing robust and efficient Go programs.

Top comments (5)

Collapse
 
bashery profile image
bashery

Useful article. Thank you.

Is:

var i int = 42
var f float64 = float64(i)
var u uint = uint(i)
Enter fullscreen mode Exit fullscreen mode

also considered a conversion?
Aren't they just new variables?

Collapse
 
zakariachahboun profile image
zakaria chahboun

Yeah, those are actually examples of type conversion in Go. Even though you're creating new variables, you're explicitly converting the value from one type to another

Collapse
 
bashery profile image
bashery

got it

Collapse
 
leoantony72 profile image
Leo Antony

Useful info, didn't know about the struct conversion

Collapse
 
zakariachahboun profile image
zakaria chahboun

You are welcome - What about function conversion?