2023.05 Vol.2

Bit of type conversions

Type Conversions

I like throwing types on everything in Go so that the compiler can help me out as much as possible. A few rules to keep in mind.

Prefer Type Definitions to Type Aliases

These things look a lot alike and I original thought maybe one of those features where either works (like in TypeScript land). Not the case here though, aliases were specifically designed for refactoring and don’t offer nearly as much help since they are not a new type like a definition. So just stick to definitions.

// Alias, just a redirection
type A = string

// Definition, full on new type
type A string

Alias vs. Definition

Don’t Fear Conversions

Go generally wants all type conversions to be explicit which might feel verbose coming from other languages where the type system does way more implicit magic. But I have come to love the explicit conversions. For example, I’ll read me code as “Right now I am treating this byte array as a Public Key which is important in this context, but later I’ll convert it to the more generic byte array to perform some low level operations”.

The rule for conversions is that the underlying base types need to be the same. This sounds a little magical, but the rules end up being pretty much what I expect them to be “Oh this is an int under the hood and so is that, I can convert it real quick”. Remember that a type in go describes the data and it’s functions, so a conversion is just changing the set of functions.

type MyInt int64
type MyOtherInt int64

func main() {
	a := MyInt(1)
	b := int64(a)
	c := MyOtherInt(b)

	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}

Prints 1 1 1

OK To Fear Arrays

Arrays and slices are always where I get tripped up. The base type conversions that I expect to work don’t, but once I remember how arrays are structured it makes more sense. In go, arrays are data chunks. The type of an array is defined by the type of thing it’s holding and the number of them. This is strict, but since arrays are copied around by value (not reference) it makes sense that a 2 item array is very different than a 1000 item array. Same goes for if an array is holding small bytes or some large structs.

A slice is a view on top of an array. So it’s more like a reference than a data object. I think this again explains why converting between a slice and an array is not as straight forward as one might first think, although possible since 1.17. Slices to arrays got some sugar in 1.20.

Type converting twice in one go is also a little tricky since type conversions are not addressable.

type Ints [2]int64

func main() {
	b := Ints(a)
	c := [2]int64(b)[:]
}

slice of unaddressable value

Type Assertions Are Kinda Lame

Type Assertions appear similar to Type Conversions at first, but I think they have almost the opposite effect. An assertion reaches under the hood of an interface to assert a certain type is being used. So where a conversion confines the type, an assertion expands it.