Go Crash Course (Part 2)

We are going to cover syntax in this section. The syntax is an essential part of programming languages. Some are similar to others. Some are distinctive into themselves. You can go to the previous section where there is the installation instruction if Go is not yet installed on your system.

  1. Variables
  2. (Basic Data) Types
  3. Pointer
  4. Struct
  5. Array
  6. Map
  7. For
  8. If
  9. Switch
  10. Defer
  11. Conclusion

Variables

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

var number, text = 9, "Lorem ipsum."

func printNumber() {
    const pi float64 = 3.141592653589793

    fmt.Println(number)
    fmt.Println(pi)
}

func printText(text string) {
    fmt.Println(text)
}

func main() {
    local := true

    printNumber()
    printText(text)
    fmt.Println(local)
}

The var statement declares a list of variables. It can include initializers, one per variable. Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type.

Constants are declared like variables, but with the const keyword, and cannot be declared using the := syntax.

As we can see, number and text are global variables because they are defined outside of a function and on the top of the program. They can be accessed inside any of the functions defined for the program.

The boolean local variable is a local variable inside the main function. It can be only accessed inside the main function.

We printed the text variable in the printText function. The variable has the same name as the global one. But the one that we printed is an argument variable. It was overwritten.

Types

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
    "fmt"
    "unsafe"
)

func main() {
    var global bool
    var text string = "Lorem ipsum."
    var negative int = -9 // int8  int16  int32  int64
    var positive uint = 9 // uint8 uint16 uint32 uint64
    var address uintptr = uintptr(unsafe.Pointer(&global))
    var ascii byte = 'a'               // alias for uint8
    var unicode rune = 'Ā'             // alias for int32
    var pi float32 = 3.141592653589793 // float32 float64
    var fullPi float64 = float64(pi)
    var complicated complex64 = 1 + 1i // complex64 complex128
    inference := 1 + complicated

    fmt.Println(global)
    fmt.Println(text)
    fmt.Println(negative)
    fmt.Println(positive)
    fmt.Println(address)
    fmt.Println(ascii)
    fmt.Println(unicode)
    fmt.Println(pi)
    fmt.Println(fullPi)
    fmt.Println(complicated)
    fmt.Println(inference)
}

Here is the output.

false
Lorem ipsum.
-9
9
824634117815
97
256
3.1415927
3.1415927410125732
(1+1i)
(2+1i)

We didn't initialize the value for the global variable. The default or zero value for boolean is false, 0 for numerics, and "" (the empty string) for strings.

The difference between int (signed integer) and uint (unsigned integer) is it can't handle negative value and has a larger maximum value for unsigned one.

The int, uint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. The number appended after the type such as int8, int16, and so on represents bits or size. 8 bits is equal to 1 byte.

The uintptr type is to store the address in number. The value is the pointer address but in an integer format.

Take a look at the pi variable. We can't store or print the actual value if the size is not enough. Then we cast float32 to float64 for fullPi to store the actual value.

The complex (complex64 or complex128) data type is to store a real and imaginary number (i symbol) of float. float32 for complex64 and float64 for complex128.

It will cast the data type automatically for shorthand. Refer to the inference variable.

The byte type is an alias for uint8. The rune type is an alias for int32. The byte is to store ASCII characters in number (0-255). The rune is to store UNICODE characters in number (-231 to 231 -1).

Pointer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
    number := 9
    pointer := &number

    fmt.Println(*pointer)
    fmt.Println(pointer)

    *pointer = number + 1

    fmt.Println(number)
}

The pointer is to store the address of a variable. The & is used to get the address of the variable. The * is used to present the value from the address of the pointed variable.

We can see that we did increment on the *pointer variable and it applied to the number variable.

Struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"

type person struct {
    name string
    age  int
}

func main() {
    p1 := person{
        "John Doe",
        1e1,
    }
    p2 := person{
        age: 999,
    }

    p1.age += 8

    fmt.Println(p1)
    fmt.Println(p1.name, "is", p1.age, "years old.")

    fmt.Println(p2)
}

A struct is a collection of fields.

Array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
    hello := [2]string{
        "Hello",
        "World!",
    }
    primes := []int{2, 3, 5}

    fmt.Println(hello)
    fmt.Println(primes)

    fmt.Println(hello[0])
    fmt.Println(primes[1:])
    fmt.Println(primes[1:2])
}

Array in Go and how we sliced the array to get the value.

Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

type location struct {
    latitude, longitude float64
}

func main() {
    offices := map[string]location{
        "Apple": {
            37.33199, -122.03089,
        },
        "Google": {
            37.42202, -122.08408,
        },
    }

    fmt.Println(offices["Apple"])
    fmt.Println("Google is at:", offices["Google"].latitude, offices["Google"].longitude)
}

Map, key-value pairs, in Go.

For

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import "fmt"

func typical() {
    for i := 1; i <= 5; i++ {
        for j := 0; j < i; j++ {
            fmt.Print("*")
        }

        fmt.Print("\n")
    }
}

func while() {
    sum := 1

    for sum < 1000 {
        sum += sum
    }

    fmt.Println(sum)
}

func main() {
    typical()
    while()
}

Go only used the for syntax for looping. Take a look at the code above about how we implement the while loop with for in Go.

If

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
    "fmt"
)

func isEven(number int) {
    if number%2 == 0 {
        fmt.Println(number, "is even.")
    } else {
        fmt.Println(number, "is odd.")
    }
}

func main() {
    isEven(1)
}

The for and if have no braces in Go.

Switch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
    "fmt"
    "time"
)

func noCondition() {
    t := time.Now()

    switch {
    case t.Hour() < 12:
        fmt.Println("Good morning!")
    case t.Hour() < 17:
        fmt.Println("Good afternoon.")
    default:
        fmt.Println("Good evening.")
    }
}

func typical(day string) {
    switch day {
    case "Monday":
        fmt.Println("Mathematics")
    case "Wednesday":
        fmt.Println("Computer Science")
    case "Friday":
        fmt.Println("Physics")
    default:
        fmt.Println("Off")
    }
}

func main() {
    typical("Today")
    noCondition()
}

The break is optional. The switch can contain no condition. It could be a clean way to write long if-then-else chains.

Defer

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
    defer fmt.Println("World!")

    fmt.Println("Hello...")

    for i := 1; i <= 3; i++ {
        defer fmt.Println(i)
    }
}

A defer statement defers the execution of a function until the surrounding function returns.

Conclusion

The reference for this article is from the official website of Go. You can practice more at https://tour.golang.org/list. You can get the source code at https://github.com/aristorinjuang/go-crash-course_02.