Go - Types, variables, functions and classes
When learning a new programming language, I think that knowing its types is very important. They are the tools that you be working with, and you should know them: strengths and use cases, conventions… Among other things, variables and functions are basic too, even if they can get complex. Then we can find some ‘advanced’ concepts in classes and testing, for example. And which I think it makes a lot of difference is to know about the standard library, there is so many power in there… Here we will start to look at types, variables, functions and classes in Go: how we define them, how we assign them, etc. Let’s dive in!
This is not intended as an exhaustive guide, it will be a bunch of concepts that I’m learning in Go, as I explore it.
Basic Types
The documentation list these basic types:
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32
// represents a Unicode code point
float32 float64
complex64 complex128
There is a lot of detail here if you are in the numeric world but for now bool
, string
, int
and float64
(higher precision than 32) are our friends. These are true or false, strings, numbers and “decimals”.
Variables
In Go, there are different ways to specify variables. There is a snippet of code that defines variables in many ways:
package main
import "fmt"
var completed = true
var path, isCommon, odds = "/home/user/file.txt", false, 0.7
var (
roundsLeft, roundsCompleted int = 2, 2
float float64
str = "Hello!"
)
func main() {
rarity, rarityID := "Common", 1
fmt.Println(rarity, rarityID)
fmt.Println(float)
}
$ go run gitlab.com/hectormartinez/variables
Common 1
0
There are a lot of things going here.
- Line 5: definition without type using the
var
keyword. - Line 6: multiple definition without type using the
var
keyword. - Line 9: multiple definition with type using a
var
block. - Line 10: definition without value using a
var
block. - Line 11: definition without type using a
var
block. - Line 15: multiple definition without type avoiding the
var
using:=
.
So here we learn that:
- Go can infer types from variable definitions.
- We can pack definitions into
var
blocks. - We can avoid the
var
keyworkd using:=
. - We can define multiple variables with different types in the same line.
- We can define a variable without value. In this case, Go will assign it a default value.
0
for floats and ints,''
(empty string) for strings andfalse
for booleans.
More complex types
After the basics, there are more complex types like struct
, array
, slice
and map
.
struct
A struct
is like a Namespace in Python, is a collection of keys and values. You can access its values using a dot and the key name.
package main
import "fmt"
// Item contains itemID, name and description
type Item struct {
itemID int
name string
description string
}
func main() {
ruby := Item{0, "Ruby", "Red gem"}
fmt.Println(ruby.itemID)
fmt.Println(ruby.description)
fmt.Println(ruby.name)
}
$ go run gitlab.com/hectormartinez/struct
0
Red gem
Ruby
array
An array
is a list of things which size is immutable. However, all its elements should be of the same type.
package main
import "fmt"
func main() {
var mylist [10]int
mylist[1] = 1
mylist[3] = 3
mylist[5] = 5
fmt.Println(mylist)
gems := [3]string{"Ruby", "Diamond", "Amethyst"}
fmt.Println(gems)
}
$ go run gitlab.com/hectormartinez/array
[0 1 0 3 0 5 0 0 0 0]
[Ruby Diamond Amethyst]
slice
A slice
is a flexible view of an array
.
package main
import "fmt"
func main() {
letters := [4]string{"a", "b", "c", "d"}
fmt.Println(letters, len(letters))
twoLetters := letters[:2]
fmt.Println(twoLetters, len(twoLetters), cap(twoLetters))
numbers := []int{1, 2, 3, 4, 5}
fmt.Println(numbers, len(numbers), cap(numbers))
numbers = numbers[1:2]
fmt.Println(numbers, len(numbers), cap(numbers))
numbers[0] = 27
fmt.Println(numbers, len(numbers), cap(numbers))
}
$ go run gitlab.com/hectormartinez/slice
[a b c d] 4
[a b] 2 4
[1 2 3 4 5] 5 5
[2] 1 4
[27] 1 4
We can define them from an array, as the letters
example, or define it as literals, as the numbers
example. When we define them, the second number inside the squared brackets is not included, so in the numbers = numbers[1:2]
will be only one number. Then, slices have length and capacity. The length is self-explanatory, about the capacity, it is the size or the underlying array
. Also, we can modify the slices and the underlying array will be modified too.
map
A map
is a like a dictionary in Python, is a collection of keys and values. However, while a struct
’s fields can be accessed with dots, map fields should be accessing using square brackets:
package main
import "fmt"
// Item contains the coordinates X and Y
type Item struct {
itemID int
name string
description string
}
func main() {
ruby := Item{0, "Ruby", "Red gem"}
diamond := Item{1, "Diamond", "White gem"}
myMap := map[string]Item{
"Ruby": ruby,
"Diamond": diamond,
}
fmt.Println(myMap)
myMap["Ruby"] = Item{3, "This was a ruby", "gem"}
myMap["NoRuby"] = Item{26, "Not a ruby", "Unkown gem"}
fmt.Println(myMap)
value, exists := myMap["Diamond"]
fmt.Println("The value key Diamond exists in myMap?", exists)
if exists {
fmt.Println("The key Diamond has a value of", value)
}
value2, exists2 := myMap["NonExisting"]
fmt.Println("The value key NonExisting exists in myMap?", exists2)
if exists {
fmt.Println("The key NonExisting has a value of", value2)
}
}
$ go run gitlab.com/hectormartinez/map
map[Ruby:{0 Ruby Red gem} Diamond:{1 Diamond White gem}]
map[Ruby:{3 This was a ruby gem} Diamond:{1 Diamond White gem} NoRuby:{26 Not a ruby Unkown gem}]
The value key Diamond exists in myMap? true
The key Diamond has a value of {1 Diamond White gem}
The value key NonExisting exists in myMap? false
The key NonExisting has a value of {0 }
Here we reuse the Item
type from our previous example to create a map of items with two keys: Diamond
and Ruby
. We can modify keys from a map simply by replacing the object in a certain key, or add a key placing an object in a new key. In Python, we can use key in dictionary
to check if a key exists, here in Go we should
try to access it and then check the proper variable (exists
and exists2
in this example).
Functions
And now we move to functions. They are another basic topic of computing languages, as they reuse code. They process input and return an output, maybe performing some side effect (as printing or writing to a file). Another snippet!:
package main
import (
"fmt"
"strings"
)
func add(x int, y int) int {
return x + y
}
func contains(str, substr string) (result bool) {
result = strings.ContainsAny(str, substr)
return
}
func main() {
mynum := add(1, 1)
bytes, err := fmt.Println(mynum)
fmt.Println(bytes)
fmt.Println(err)
result := contains("MyString", "g")
fmt.Println(result)
}
$ go run gitlab.com/hectormartinez/function
2
2
<nil>
true
Functions start with func
, a name, the input, the output and the body. Here, we defined a func named add
which takes x
(which is an int
),
We defined the function add
, that takes x and y, both integers, and return another integer, which is the sum of both x and y. Then we save its result calling it with two
ones, and pass the result to the fmt.Println
function. That function return two things, an integer with the number of bytes written and an error.
Classes
Go does not have classes (from Go Tour). What?! Yeah, that’s all, section finished.
package main
func main() {
}
Methods
However, you can define methods on types (from Go Tour too). Better now. So say we want to have a Path
type, which can be joined with others, as pathlib.Path
in Python.
package main
import (
"fmt"
"path/filepath"
)
// Path contain items
type Path string
func (path Path) join(other Path) (finalPath Path) {
finalPath = path + Path(string(filepath.Separator)) + other
return finalPath
}
func main() {
usr := Path("/usr")
lib := Path("lib")
fmt.Println(usr)
fmt.Println(lib)
fmt.Println(usr.join(lib))
}
$ go run gitlab.com/hectormartinez/class
/usr
lib
/usr/lib
Here we import another package other than “fmt”, which is “path/filepath” that gives utilities for paths management. Then we implement the type Path
which comes from a string
. Then we define a function and as a first element, we put (path Path)
, so Go knows that is a method of Path
. In that element, we define the name of the “class” inside the body function, which will be path
.
Summary
Here we took a fast view of the basic and more complex types and structures of Go. We learned how to assign variables and built that complex structures. Also, we closed with the definition of functions and methods. In the following articles about Go, we will be taking a look at interfaces, testing and the standard library… Stay tuned!
It was useful? Done something similar? Have feedback?