Golang
Summary
- Introduction
- Basic
- Advanced
- Standard Libs
Hello World
1package main
2
3import "fmt"
4
5func main() {
6 fmt.Println("Hello Gophers!")
7}
Go CLI Commands
1# Compile & Run code
2$ go run [file.go]
3
4# Compile
5$ go build [file.go]
6# Running compiled file
7$ ./hello
8
9# Test packages
10$ go test [folder]
11
12# Install packages/modules
13$ go install [package]
14
15# List installed packages/modules
16$ go list
17
18# Update packages/modules
19$ go fix
20
21# Format package sources
22$ go fmt
23
24# See package documentation
25$ go doc [package]
26
27# Add dependencies and install
28$ go get [module]
29
30# See Go environment variables
31$ go env
32
33# See version
34$ go version
Go Modules
- Go projects are called modules
- Each module has multiple packages
- Each package should has a scoped functionality. Packages talk to each other to compose the code
- A module needs at least one package, the main
- The package main needs a entry function called main
1# Create Module
2$ go mod init [name]
Tip: By convention, modules names has the follow structure:
domain.com/user/module/package
Example: github.com/spf13/cobra
Basic Types
Type | Set of Values | Values |
---|---|---|
bool | boolean | true/false |
string | array of characters | needs to be inside "" |
int | integers | 32 or 64 bit integer |
int8 | 8-bit integers | [ -128, 128 ] |
int16 | 16-bit integers | [ -32768, 32767] |
int32 | 32-bit integers | [ -2147483648, 2147483647] |
int64 | 64-bit integers | [ -9223372036854775808, 9223372036854775807 ] |
uint8 | 8-bit unsigned integers | [ 0, 255 ] |
uint16 | 16-bit unsigned integers | [ 0, 65535 ] |
uint32 | 32-bit unsigned integers | [ 0, 4294967295 ] |
uint64 | 64-bit unsigned integers | [ 0, 18446744073709551615 ] |
float32 | 32-bit float | |
float64 | 64-bit float | |
complex64 | 32-bit float with real and imaginary parts | |
complex128 | 64-bit float with real and imaginary parts | |
byte | sets of bits | alias for uint8 |
rune | Unicode characters | alias for int32 |
Variables
1// Declaration
2var value int
3
4// Initialization
5value = 10
6
7// Declaration + Initialization + Type inference
8var isActive = true
9
10// Short declaration (only inside functions)
11text := "Hello"
12
13// Multi declaration
14var i, j, k = 1, 2, 3
15
16// Variable not initialized = Zero values
17// Numeric: 0
18// Boolean: false
19// String: ""
20// Special value: nil (same as null)
21
22var number int // 0
23var text string // ""
24var boolean bool // false
25
26// Type conversions
27// T(v) converts v to type T
28
29i := 1.234 // float
30int(i) // 1
31
32// Constants
33const pi = 3.1415
Operators
Arithmetic Operators
Symbol | Operation | Valid Types |
---|---|---|
+ |
Sum | integers, floats, complex values, strings |
- |
Difference | integers, floats, complex values |
* |
Product | integers, floats, complex values |
/ |
Quotient | integers, floats, complex values |
% |
Remainder | integers |
& |
Bitwise AND | integers |
` | ` | Bitwise OR |
^ |
Bitwise XOR | integers |
&^ |
Bit clear (AND NOT) | integers |
<< |
Left shift | integer « unsigned integer |
>> |
Right shift | integer » unsigned integer |
Comparison Operators
Symbol | Operation |
---|---|
== |
Equal |
!= |
Not equal |
< |
Less |
<= |
Less or equal |
> |
Greater |
>= |
Greater or equal |
Logical Operators
Symbol | Operation |
---|---|
&& |
Conditional AND |
` | |
! |
NOT |
Conditional Statements
1// If / Else
2i := 1
3
4if i > 0 {
5 // Condition is True! i is greater than zero
6} else {
7 // Condition is False! i is lower or equal to zero
8}
9
10// Else if
11i := 1
12
13if i > 0 {
14 // Condition is True! i is greater than zero
15} else if i > 0 && i < 2 {
16 // Condition is True! i greater than zero and lower than two
17} else if i > 1 && i < 4 {
18 // Condition is True! i greater than one and lower than four
19} else {
20 // None of the above conditions is True, so it falls here
21}
22
23// If with short statements
24i := 2.567
25
26if j := int(i); j == 2 {
27 // Condition is True! j, the integer value of i, is equal to two
28} else {
29 // Condition is False! j, the integer value of i, is not equal to two
30}
31
32// Switch
33text := 'hey'
34
35switch text {
36 case 'hey':
37 // 'Hello!'
38 case 'bye':
39 // 'Byee'
40 default:
41 // 'Ok'
42}
43
44// Switch without condition
45value := 5
46
47switch {
48 case value < 2:
49 // 'Hello!'
50 case value >= 2 && value < 6:
51 // 'Byee'
52 default:
53 // 'Ok'
54}
Loops
1// Golang only has the for loop
2for i := 0; i < 10; i++ {
3 // i
4}
5
6// The first and third parameters are ommitable
7// For as a while
8i := 0;
9
10for i < 10 {
11 i++
12}
13
14// Forever loop
15for {
16
17}
Arrays
1// Declaration with specified size
2var array [3]string
3array[0] = "Hello"
4array[1] = "Golang"
5array[2] = "World"
6
7// Declaration and Initialization
8values := [5]int{1, 2, 3, 4, 5}
9
10// Slices: A subarray that acts as a reference of an array
11// Determining min and max
12values[1:3] // {2, 3, 4}
13
14// Determining only max will use min = 0
15values[:2] // {1, 2, 3}
16
17// Determining only min will use max = last element
18values[3:] // {3, 4}
19
20// Length: number of elements that a slice contains
21len(values) // 5
22
23// Capacity: number of elements that a slice can contain
24values = values[:1]
25len(values) // 2
26cap(values) // 5
27
28// Slice literal
29slice := []bool{true, true, false}
30
31// make function: create a slice with length and capacity
32slice := make([]int, 5, 6) // make(type, len, cap)
33
34// Append new element to slice
35slice := []int{ 1, 2 }
36slice = append(slice, 3)
37slice // { 1, 2, 3 }
38slice = append(slice, 3, 2, 1)
39slice // { 1, 2, 3, 3, 2, 1 }
40
41// For range: iterate over a slice
42slice := string["W", "o", "w"]
43
44for i, value := range slice {
45 i // 0, then 1, then 2
46 value // "W", then "o", then "w"
47}
48
49// Skip index or value
50
51for i := range slice {
52 i // 0, then 1, then 2
53}
54
55for _, value := range slice {
56 value // "W", then "o", then "w"
57}
Functions
1// Functions acts as a scoped block of code
2func sayHello() {
3 // Hello World!
4}
5sayHello() // Hello World!
6
7// Functions can take zero or more parameters, as so return zero or more parameters
8func sum(x int, y int) int {
9 return x + y
10}
11sum(3, 7) // 10
12
13// Returned values can be named and be used inside the function
14func doubleAndTriple(x int) (double, triple int) {
15 double = x * 2
16 triple = x * 3
17 return
18}
19d, t := doubleAndTriple(5)
20// d = 10
21// t = 15
22
23// Skipping one of the returned values
24_, t := doubleAndTriple(3)
25// t = 9
26
27// Functions can defer commands. Defered commands are
28// runned in a stack order after the execution and
29// returning of a function
30var aux = 0
31
32func switchValuesAndDouble(x, y int) {
33 aux = x
34 defer aux = 0 // cleaning variable to post use
35 x = y * 2
36 y = aux * 2
37}
38
39a, b = 2, 5
40switchValuesAndDouble(2, 5)
41
42// a = 10
43// b = 4
44// aux = 0
45
46// Functions can be handled as values and be anonymous functions
47func calc(fn func(int, int) int) int {
48 return fn(2, 6)
49}
50
51func sum(x, y int) int {
52 return x + y
53}
54
55func mult(x, y int) int {
56 return x * y
57}
58
59calc(sum) // 8
60calc(mult) // 12
61calc(
62 func(x, y int) int {
63 return x / y
64 }
65) // 3
66
67// Function closures: a function that returns a function
68// that remembers the original context
69func calc() func(int) int {
70 value := 0
71 return func(x int) int {
72 value += x
73 return value
74 }
75}
76
77calculator := calc()
78calculator(3) // 3
79calculator(45) // 48
80calculator(12) // 60
Structs
Structs are a way to arrange data in specific formats.
1// Declaring a struct
2type Person struct {
3 Name string
4 Age int
5}
6
7// Initializing
8person := Person{"John", 34}
9person.Name // "John"
10person.Age // 34
11
12person2 := Person{Age: 20}
13person2.Name // ""
14person2.Age // 20
15
16person3 := Person{}
17person3.Name // ""
18person3.Age // 0
Maps
Maps are data structures that holds values assigneds to a key.
1// Declaring a map
2var cities map[string]string
3
4// Initializing
5cities = make(map[string]string)
6cities // nil
7
8// Insert
9cities["NY"] = "EUA"
10
11// Retrieve
12newYork = cities["NY"]
13newYork // "EUA"
14
15// Delete
16delete(cities, "NY")
17
18// Check if a key is setted
19value, ok := cities["NY"]
20ok // false
21value // ""
Pointers
Pointers are a direct reference to a memory address that some variable or value is being stored.
1// Pointers has *T type
2var value int
3var pointer *int
4
5// Point to a variable memory address with &
6value = 3
7pointer = &value
8
9pointer // 3
10pointer = 20
11pointer // 20
12pointer += 5
13pointer // 25
14
15// Pointers to structs can access the attributes
16type Struct struct {
17 X int
18}
19
20s := Struct{3}
21pointer := &s
22
23s.X // 3
Obs: Unlike C, Go doesn’t have pointer arithmetics.
Methods and Interfaces
Go doesn’t have classes. But you can implement methods, interfaces and almost everything contained in OOP, but in what gophers call “Go Way”
1type Dog struct {
2 Name string
3}
4
5func (dog *Dog) bark() string {
6 return dog.Name + " is barking!"
7}
8
9dog := Dog{"Rex"}
10dog.bark() // Rex is barking!
Interfaces are implicitly implemented. You don’t need to inform that your struct are correctly implementing a interface if it already has all methods with the same name of the interface.
All structs implement the interface{}
interface. This empty interface means the same as any
.
1// Car implements Vehicle interface
2type Vehicle interface {
3 Accelerate()
4}
5
6type Car struct {
7
8}
9
10func (car *Car) Accelerate() {
11 return "Car is moving on ground"
12}
Errors
Go doesn’t support throw
, try
, catch
and other common error handling structures. Here, we use error
package to build possible errors as a returning parameter in functions
1import "errors"
2
3// Function that contain a logic that can cause a possible exception flow
4func firstLetter(text string) (string, error) {
5 if len(text) < 1 {
6 return nil, errors.New("Parameter text is empty")
7 }
8 return string(text[0]), nil
9}
10
11a, errorA := firstLetter("Wow")
12a // "W"
13errorA // nil
14
15b, errorB := firstLetter("")
16b // nil
17errorB // Error("Parameter text is empty")
Testing
Go has a built-in library to unit testing. In a separate file you insert tests for functionalities of a file and run go test package
to run all tests of the actual package or go test path
to run a specific test file.
1// main.go
2func Sum(x, y int) int {
3 return x + y
4}
5
6// main_test.go
7import (
8 "testing"
9 "reflect"
10)
11
12func TestSum(t *testing.T) {
13 x, y := 2, 4
14 expected := 2 + 4
15
16 if !reflect.DeepEqual(sum(x, y), expected) {
17 t.Fatalf("Function Sum not working as expected")
18 }
19}
Concurrency
One of the main parts that make Go attractive is its form to handle with concurrency. Different than parallelism, where tasks can be separated in many cores that the machine processor have, in concurrency we have routines that are more lightweight than threads and can run asynchronously, with memory sharing and in a single core.
1// Consider a common function, but that function can delay itself because some processing
2func show(from string) {
3 for i := 0; i < 3; i++ {
4 fmt.Printf("%s : %d\n", from, i)
5 }
6}
7
8// In a blocking way...
9func main() {
10 show("blocking1")
11 show("blocking2")
12
13 fmt.Println("done")
14}
15/* blocking1: 0
16 blocking1: 1
17 blocking1: 2
18 blocking2: 0
19 blocking2: 1
20 blocking2: 2
21 done
22*/
23
24// Go routines are a function (either declared previously or anonymous) called with the keyword go
25func main() {
26 go show("routine1")
27 go show("routine2")
28
29 go func() {
30 fmt.Println("going")
31 }()
32
33 time.Sleep(time.Second)
34
35 fmt.Println("done")
36}
37
38/* Obs: The result will depends of what processes first
39 routine2: 0
40 routine2: 1
41 routine2: 2
42 going
43 routine1: 0
44 routine1: 1
45 routine1: 2
46 done
47*/
48
49// Routines can share data with channels
50// Channels are queues that store data between multiple routines
51msgs := make(chan string)
52
53go func(channel chan string) {
54 channel <- "ping"
55}(msgs)
56
57go func(channel chan string) {
58 channel <- "pong"
59}(msgs)
60
61fmt.Println(<-msgs) // pong
62fmt.Println(<-msgs) // ping
63
64// Channels can be bufferized. Buffered channels will accept a limited number of values and when someone try to put belong their limit, it will throw and error
65numbers := make(chan int, 2)
66
67msgs<-0
68msgs<-1
69msgs<-2
70
71// fatal error: all goroutines are asleep - deadlock!
72
73// Channels can be passed as parameter where the routine can only send or receive
74numbers := make(chan int)
75
76go func(sender chan<- int) {
77 sender <- 10
78}(numbers)
79
80go func(receiver <-chan int) {
81 fmt.Println(<-receiver) // 10
82}(numbers)
83
84time.Sleep(time.Second)
85
86// When working with multiple channels, the select can provide a control to execute code accordingly of what channel has bring a message
87c1 := make(chan string)
88c2 := make(chan string)
89
90select {
91case msg1 := <-c1:
92 fmt.Println("received", msg1)
93case msg2 := <-c2:
94 fmt.Println("received", msg2)
95default:
96 fmt.Println("no messages")
97}
98
99go func() {
100 time.Sleep(1 * time.Second)
101 c1 <- "channel1 : one"
102}()
103go func() {
104 time.Sleep(2 * time.Second)
105 c2 <- "channel2 : one"
106}()
107
108for i := 0; i < 2; i++ {
109 select {
110 case msg1 := <-c1:
111 fmt.Println("received", msg1)
112 case msg2 := <-c2:
113 fmt.Println("received", msg2)
114 }
115}
116
117/*
118 no messages
119 received channel1: one
120 received channel2: one
121*/
122
123// Channels can be closed and iterated
124channel := make(chan int, 5)
125
126for i := 0; i < 5; i++ {
127 channel <- i
128}
129
130close(channel)
131
132for value := range channel {
133 fmt.Println(value)
134}
135
136/*
137 0
138 1
139 2
140 3
141 4
142*/
Package fmt
1import "fmt"
2
3fmt.Print("Hello World") // Print in console
4fmt.Println("Hello World") // Print and add a new line in end
5fmt.Printf("%s is %d years old", "John", 32) // Print with formatting
6fmt.Errorf("User %d not found", 123) // Print a formatted error