This is the third entry of my weekly series Learning Go. Last week I covered control flow and a few of the primary control structures. I also touched on familiar territory; loops, the for statement, and conditional statements. This week I am diving head-first into types in Go, and what they are most commonly used with.
determines the set of values together with operations and methods that are specific to those values
Think of a type like a family. They are a collection of like values, much like a family is a collection of folks who share genetics, traits, etc. In the same way, types can share operations and methods that only that type has access to.
Most commonly used types in Go are as follows:
These are some more commonly used types, we will cover these in a later post:
To create a new type, you simply use the type keyword:
type (
LightsaberColor string
)
A new type, LightsaberColor
has now been created in your program. A few things to note that is happening in this declaration:
LightsaberColor
) to a type name, string
.An alias declaration also binds an identifier to the given type, and are not meant for everyday use. Their purpose was to support large refactors involving moving a type between multiple and/or large packages.
You can create an alias declaration like this:
type (
LightsaberColor = JediLightsaberColor
)
Notice that these types now denote the same type. JediLightsaberColor
can now be used as a replacement for LightsaberColor
.
Every type has what is called an underlying type. This is essentially the underbelly or the source of types that is bound to the declared type. Let me give you an example:
type (
BountyHunter string
)
This underlying type of BountyHunter
is string
. Common types such as: bool
, string
, int
are called predeclared identifiers. This just means that they are implicitly declared in the universe block (encompasses all Go source text).
When discussing types, two can only be identical or different. Let me give you a few examples that will help differentiate the two.
Identical
package main
import (
"fmt"
)
type (
Jedi = bool
Yoda = bool
)
var j Jedi
var y Yoda
func main() {
fmt.Printf("%T\n", j)
// bool
fmt.Printf("%T", y)
// bool
}
Jedi
is of the type bool
, Yoda
is also of type bool
. This is also an example of an alias declaration.Different
package main
import (
"fmt"
)
type (
Sith = string
)
type (
Emperor = Sith
Yoda string
)
var s Sith
var e Emperor
var y Yoda
func main() {
fmt.Printf("%T\n", s)
// string
fmt.Printf("%T\n", e)
// string
fmt.Printf("%T", y)
// main.Yoda
}
Yoda
and Emperor
have the same underlying types, they were created in separate type definitionscreates a new, distinct type with the same underlying type and operations as the given type and binds an identifier to it
Let me break down this definition by showing you how to create a type definition
type (
HasTheForce bool
)
Here we have done a few things:
HasTheForce
HasTheForce
to itthis distinct type has the same type and its underlying type: bool
represents a set of truth values denoted by predeclared constants
true
andfalse
Note: the predeclared boolean
type is bool
represents a set of integers or floating-point values
These types get pretty extensive, for now, I will show the differences between integers and floating-point.
Integers
package main
import (
"fmt"
)
func main() {
x := 42
fmt.Printf("%T", x)
// int
}
Floating Point
package main
import (
"fmt"
)
func main() {
x := 42.12345
fmt.Printf("%T", x)
// float64
}
represents a set of string values, a string value is a (potentially empty) sequence of bytes
Things to note:
Note: the predeclared string
type is string
func main() {
x := "do or do not, there is no try"
fmt.Printf("%T", x)
// string
}
You can get the length of a string by using the built-in function len
:
func main() {
x := "do or do not, there is no try"
fmt.Println(len(x))
// 29
}
numbered sequence of a single type (element type)
Things to note:
int
Here is an example of an array:
func main() {
var a [10]int
fmt.Printf("%T", a)
// [10]int
}
Note: we use the var
keyword here because we are declaring an array of type int
, we can not use the short variable declaration (:=
) here because we are binding a type to a
.
You can get the length of an array by using the built-in function len
(just like with strings):
func main() {
var a [10]int
fmt.Println(len(a))
// 10
}
Note: I am also passing the length
of the array by placing the value 10
in between the brackets.
I have found that arrays are not commonly used in Go, slice
is far more common.
continuous segments with an underlying type of array and gives access to a numbered sequence
Essentially, a slice allows you to gather values of the same type.
A common practice in declaring a slice is by using a composite literal
.
A composite literal is composed of a few things, the type and the value.
package main
import (
"fmt"
)
func main() {
// composite literal
cl := []int{4, 5, 6, 7, 8}
fmt.Println(cl)
// [1 2 3 4]
}
Let me explain what is happening:
cl
using a short variable declarationslice
that will contain the type int
([]int
)1 2 3 4
to cl
by placing them between brackets {}
Note: when creating a composite literal all values have to be of the same type
Because slice
is built on top of an array, when the length of that slice changes a few things happen:
As you can assume, this can potentially take a lot of processing power. To avoid this, we can use the make
function.
package main
func main() {
a := make([]int, 10 , 10)
fmt.Println(a)
// [0 0 0 0 0 0 0 0 0 0]
}
A few things to note:
make
’s first parameter has to be the slice typemake
’s second parameter is the lengthmake
’s third and optional parameter is the capacity of the slice
Let’s see it in action.
package main
import (
"fmt"
)
func main() {
x := make([]int, 5, 5)
fmt.Println(x)
// [0 0 0 0 0]
}
As you can see the slice length and capacity is five and will print five 0
values. What happens if we try to go over the slice capacity?
package main
import (
"fmt"
)
func main() {
x := make([]int, 5, 5)
x[5] = 6
fmt.Println(x)
// [0 0 0 0 0]
}
Here, I am attempting to manually assign a value to the 5th indexed position of this slice. When I do I get this compile error:
panic: runtime error: index out of range [5] with length 5
Go not only prevents us from going off the rails with our code, but gives us precise control over our data.
There are so many more features of slice
I want to discuss, and what’s funny is, I have not even touched on map
or struct
yet. I like to keep these posts at a 5-10 minute read; therefore, my next entry will be more on slice
, map
, and struct
. I am excited to share what I have learned with you. Until next time!