DEV Community

Cover image for Go Crash Course Part IV: Types, Conversion and Inference
Mofi Rahman
Mofi Rahman

Posted on • Originally published at blogs.moficodes.dev

Go Crash Course Part IV: Types, Conversion and Inference

The code for this part is in the types folder under this github repo.

Types

Go is a typed language. That means everything we create must have a type.

There are a few basic types.

Boolean Types

Represents set of boolean value. With predefined constant true and false

bool        true or false
Enter fullscreen mode Exit fullscreen mode

Numeric Types

Integer and Floating point values. Following are architecture independent numeric types.


uint8       the set of all unsigned  8-bit integers (0 to 255)
uint16      the set of all unsigned 16-bit integers (0 to 65535)
uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)

int8        the set of all signed  8-bit integers (-128 to 127)
int16       the set of all signed 16-bit integers (-32768 to 32767)
int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

float32     the set of all IEEE-754 32-bit floating-point numbers
float64     the set of all IEEE-754 64-bit floating-point numbers

complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts

byte        alias for uint8
rune        alias for int32
Enter fullscreen mode Exit fullscreen mode

These are architecture dependent. (Could be either 32 bit or 64 bit)

uint     either 32 or 64 bits
int      same size as uint
uintptr  an unsigned integer large enough to store the uninterpreted bits of a pointer value
Enter fullscreen mode Exit fullscreen mode

String Types

String is a sequence of bytes.

Composite Types

Array

Arrays are fixed length and fixed type.

var arr = [5]int{}
Enter fullscreen mode Exit fullscreen mode

Slice

Contiguous segment of underlying array. Has fixed type. Length can be dynamically reallocated. (More on this on a later chapter)

var slice = []int{}
Enter fullscreen mode Exit fullscreen mode

Maps

Key value store. Similar to dictionary in python or object in JavaScript. But it still has a fixed type.

var maps = map[int]int{}
Enter fullscreen mode Exit fullscreen mode

Functions

Functions are also types.

var function = func() int {
    return 42
}
Enter fullscreen mode Exit fullscreen mode

function has a type func() int

Struct

Custom type defined by user. Its a sequence of named elements with name and types. Struct can be empty too.

type empty struct{}
var x = empty{}
Enter fullscreen mode Exit fullscreen mode

Channel

Channels are typed conduit between running go routines. We will hold off on talking about channels until we talk about go routines.

Interface

An interface type specifies a method set called its interface. If a any type has the method set of the interface, that type implements the interface. All types implement the empty interface interface{}. Interface does not have any type. Its always the underlying components type. A type can implement any number of interfaces. We will talk about interface in great details in a later post.

var y interface{} = x // y now has a type of empty since that is the type of x.
Enter fullscreen mode Exit fullscreen mode

Type Alias and Type Definition

We already saw 2 examples of type aliases. byte and rune are type aliases of uint8 and uint32 respectively. This means every time we use byte we are basically using an uint8. We can also define our own type from another base type.

// type alias
type myFloat = float64
var aliasFloat myFloat = 5.0 // has type float64
// type definition
type myint int
var aliasInt myint = 1 // has type myint
Enter fullscreen mode Exit fullscreen mode

Type alias is useful for moving types between packages for refactoring. Lets say we were using a struct type called coolstuff in a package. If we move the coolstuff to a different package, we would have to refactor all the places where coolstuff were being used. One way to mitigate issues during the refactor type is to create a type alias in the old package where coolstuff was.

type coolstuff = newpackage.Coolstuff
Enter fullscreen mode Exit fullscreen mode

Type definition is used usually for creating enums. Or sometimes to extend existing types by attaching methods. For example:

func (m myint) double() myint {
    return m * 2
}
Enter fullscreen mode Exit fullscreen mode

Type int does not have any methods. But with myint we can create a double() method on our type. We have yet to talk about method and method receiver. We will talk about it in depth.

Type Conversion

Basic numeric types can be converted with T() syntax. Where T is the type to be converted to.

var i int = 355        // type int
f := float64(i)        // type float64
by := byte(i)          // type byte (uint8)
Enter fullscreen mode Exit fullscreen mode

Struct type can be converted to and from each other as long as they have identical field names and types in identical order.

type person struct {
    age  int
    name string
}

type student struct {
    age  int // try swapping these fields
    name string
}

p := person{age: 20, name: "john"}
std := student{age: 15, name: "paul"}

p2 := person(std)
s2 := student(p)
Enter fullscreen mode Exit fullscreen mode

There is a way to type case any struct to any other struct using the unsafe package. This is rarely used. If you write code that uses unsafe package, you better have a solid reasoning for it.

On function calls type conversion is automatic if the parameter can be converted to the desired type it gets converted. int will be converted to float but not the other way around because float to int can loose precision.

For defined types, the conversion is uni directional. You can always use the underlying type in place of the defined type but not the other way around.

func definedTypeConversion(mi myint) {
    fmt.Printf("Type of mi=%T\n", mi)
}

func takesInt(i int) {
    fmt.Printf("Type of i=%d", i)
}

definedTypeConversion(aliasInt) 
definedTypeConversion(5) // this get converted to myint type

takesInt(5)
takesInt(aliasInt) // this does not work
Enter fullscreen mode Exit fullscreen mode

Inference

We have seen example of inference a few times already. Every time we use the := operator, we are inferring the type of the left hand operand.

x := 5 // type inferred to int
y := x // type inferred to int because of the type of x
Enter fullscreen mode Exit fullscreen mode

Type inference also happens for numeric constants when passing to a function if the compiler does not detect a loss of precision or overflow.

Next Steps

This is Part 4 of this Go crash course series.

Top comments (0)