Basics

Notes on the GO Programming language

Updated: 25 June 2025

Installation

Follow the installation on the Go Docs for your operating system

Once done with that, you will need to add ~/go/bin to your PATH

Hello World

First, create a new directory for your project. In it create a module to enable dependency tracking - you can do this with:

Terminal window
1
go mod init example/hello

Make a new project directory with a file called main.go with the following content

Go has a main function that needs to be created for every package and it will be run automatically. We also need to import fmt to print content to the screen. Also note that Go strings must have double quotes "hello" or we will get an error

1
package main
2
3
import "fmt"
4
5
func main() {
6
fmt.Println("Hello World")
7
}

We can run this with the following command from the project dir

Terminal window
1
go run main.go

Or since it’s a module, we can just do:

Terminal window
1
go run .

Build and Run

We can build binaries for an application with go install which can then be executed from the terminal

The simplest way to build a program is using go build which will build it and output it in the current directory

For example, we can build and run it as follows:

Terminal window
1
go build
2
3
./hello

You can also install the package globally with:

Terminal window
1
go install

Which will add this to ~/go/bin and can just be run using:

Terminal window
1
hello

Variables and Data Types

Go has the following data types

  • string
  • bool
  • int int8 int16 int32 int64
  • uint uint8 uint16 uint32 uint64
  • byte (uint8)
  • rune (int32)
  • float32 float64
  • complex64 complex128

There are a few different ways to create variables, note that if we create a variable and do not use it we will get an error

Var

1
var name string = "John"

Or with a separate declaration and initialization:

1
var age int
2
3
age = 5

Type Inference

1
var name = "John"

Get Type

We can make use of the following function to get the type of a variable

1
fmt.Printf("%T",name)

Constants

We can define constants with the const keyword

1
const name = "John"

Global Variables

We can declare global variables by defining them outside of the main function

Shorthand Method

Inside of a function we can declare variables using the assignment operator with the following without having to specify the type of var keyword

1
name := "John

Multiple Assignments

We can do multiple assignments as follows

1
name, age := "John", 15

Booleans

Booleans in Go a represented using a bool type and are either true or false

1
var online = true
2
3
var active bool

Booleans are implicily initialized as false

The boolean operators are:

  • && - and
  • || - or
  • ! - not

Numbers

Go has basic numeric types for representing integers and floating point values.

Some of these are int, float64, and uint and conversion between types can be done using functions with the name of the respective type. For example:

1
var x int = 5
2
var y = float64()

Go supports the normal numerical operations such as +, -, *, /, and %. Note that for integer division the number is truncated back to an int

Numeric operations are only supported between numbers of the same type

Strings

string is an immutable sequence of bytes and can be defined using double quotes:

1
var str1 = "Hello"
2
str2 := "World"

Concatenation of strings can be done using + like so:

1
str3 := str1 + " " + str2

The strings package also has many methods for working with strings:

1
import "strings"
2
3
func caps(name string) {
4
strings.ToUpper(name)
5
}

String Formatting

String formatting can be done using the fmt package which can format strings using fmt.Sprintf like so:

1
str := fmt.Sprintf("int %d, int %03d, float %.3f, string %s, props %+v", 5, 7, 1.5, "hello", myStruct)
2
// str == "int 5, int 007, float 1.500, string hello, props { Name "hello" }"

Lots of other formatting options also exist for string formatting and can be found in the docs

Packages

Importing Packages

We can import multiple packages by declaring each package in the import statement on a different line

1
import (
2
"fmt"
3
"math"
4
)

Dependencies

We can add dependencies to our Go module by simply importing them in the code that needs it, for example:

Terminal window
1
import "rsc.io/quote"

And then Go can automatically add it to our go.mod with:

Terminal window
1
go mod tidy

Creating Packages

Create a new folder and in it you can define the package name and some functions in it

1
package mypackage
2
3
func myfunction() {
4
...
5
}

And then import the package by referring to its path in the import function

Functions

Functions are defined with the func keyword, the function name, inputs, and return types and values

1
func greet(name string) string {
2
return "Hello " + name
3
}

The convention is to start functions with an uppercase letter if they’re public and lowercase if they’re private

Functions can also have multiple input parameters. If these are of the same type the type can just be specified once:

1
go canMessage(visible, online bool) bool {
2
return visible && online
3
}

Variadic Functions

A variadic function is a function that takes a variable number of arguments, this is done using ... in the type of the function:

1
func formatMany(format string, strs ...string) string {
2
result := ""
3
4
for _, str := range strs {
5
result += " " + fmt.Sprintf(format, str)
6
}
7
8
return result
9
}
10
11
func main() {
12
str := formatMany("Hello %s", "John", "Alice", "Bob")
13
fmt.Println(str)
14
}

The variadic parameter must be the last in the parameter list

A slice can also be passed into a variadic function using ... after the name of the variable:

1
func main() {
2
names := []string{"John", "Alice", "Bob"}
3
str := formatMany("Hello %s", names...)
4
}

Arrays and Slices

In Go arrays are fixed length, and a slice is an array without a fixed size

Arrays

Arrays are fixed in size and can be defined with

1
var myArray [3]string
2
3
myArray[0] = "Hello"
4
myArray[2] = "Word"

Or with initial values

1
myArr := [2]string{"Hello", "World"}

Slices

A slice is essentially an array that does not have a fixed size

1
mySlice := []string{"Hello", "World","!"}

We can also make slices by using the same notation as other languages

1
newSlice := mySlice[2:5]

Elements can be added to a slice using the append function:

1
mySlice := []int{1,2,3}
2
mySlice = append(mySlice, 4,5,6)

Note that append is not a pure function and the original slice may be modified

Conditionals

Conditionals do not require parenthesis, however they can be used

If Else

1
if x < y {
2
...
3
} else if x == y {
4
...
5
} else {
6
...
7
}

Swtich/Case

1
switch x {
2
case: 5:
3
...
4
case 10:
5
...
6
default:
7
...
8
}

Loops

There are two methods for building for loops

1
i := 1
2
for i <= 01 {
3
...
4
i++
5
}
1
for i := 1; i <= 10; i++ {
2
...
3
}

Maps

Maps are key-value pairs and can be defined and accessed with

1
ages := make(map[string]int)
2
3
ages["Bob"] = 35
4
ages["John"] = 5

We can delete a value from a map with the delete function

1
delete(emails, "Bob")

We can also declare a map with initial values

1
emails := map[string]int{"Bob":35, "John":5}

Range

Range is used for looping through values

1
ids := []int{1,2,3}
2
3
for i, id := range ids{
4
...
5
}

If we are not using the i variable, we can use an _ to receive any inputs that we will not use

1
for _, id := range ids{
2
...
3
}
1
emails := map[string]int{"Bob":35, "John":5}
2
3
for k, v := range emails {
4
...
5
}

Pointers

A pointer allows us to point to the memory address of a specific value, we can get the pointer for a value with the & sign

1
a := 5
2
b := &a

If we wan to get back from the pointer to the actual value we can do that with the * operator

1
a == *b // true
2
a == *&a //true

We can modify the value of a pointer

1
*b = 10
2
a == 10 // true

The reason to use pointers can be more efficient when passing values

Closures

We can define anonymous functions to declare anonymous functions that can be used as closures

1
func adder () func (int) int {
2
sum := 0
3
return func(x int) int {
4
sum += x
5
return sum
6
}
7
}
8
9
10
func main() {
11
sum := adder()
12
for i:= 0; i < 10; i++ {
13
fmt.Println(sum(i)) // 0 1 3 6 ...
14
}
15
}

Structs

Structs are like classes

Structs can contain values and functions, of which we can have value reveivers and pointer receivers. Value receivers just do calculations, Pointer Receivers modify data

1
type Person struct {
2
firstName string
3
lastName string
4
age int
5
}

Structs can be instantiated using all the property names or using the values provided in the struct order:

1
person1 := Person{firstName: "John", lastName: "Smith", age: 25}
2
3
person2 := Person{"John", "Smith", 25}

new struct

In Go we can also create an instance of a struct where each value has the default value using the new function:

1
// possible but not recommended as the values are zero-initialized so relatively meaningless
2
person := new(Person)

Methods

These are functions that can be called on a struct directly using a Receiver argument that is defined after the func keyword:

1
func (p Person) myValueReceiver() string {
2
return "Hello " + p.firstName
3
}
4
5
func (p *Person) myPointerReceiver() {
6
p.age++
7
}

They can then be called using the syntax as seen below

1
func main() {
2
person := Person{firstName: "John", lastName: "Smith", age: 25}
3
person2 := Person{"John", "Smith", 25}
4
5
person.firstName // John
6
7
person.myValueReceiver() // Hello John
8
person.myPointerReceiver() // person.age = 26
9
}

Interfaces

1
type Shape interface {
2
area() float64
3
}
4
5
type Circle struct {
6
x, y, radius float64
7
}
8
9
type Rectangle struct {
10
width, height float64
11
}
12
13
func (c Circle) area() float64 {
14
return math.Pi * c.radius * c.radius
15
}
16
17
func (r Rectangle) area() float64 {
18
return r.width * r.height
19
}
20
21
func getArea(s Shape) float64 {
22
return s.area()
23
}

Web

To work with HTTP requests we can use thenet/http package which allows us to define a function to handle a specific route

1
package main
2
3
import ("fmt"
4
"net/http"
5
)
6
7
func main(){
8
http.HandleFunc("/", index)
9
fmt.Println("Server Starting")
10
http.ListenAndServe(":3000", nil)
11
}
12
13
func index(w http.ResponseWriter, r *http.Request) {
14
fmt.Fprintf(w, "Hello World")
15
}

Randomness

The math/rand package offers some utilities for generaating random numbers

Numbers generated by math/rand are not truly random and can be seeded before retreiving random numbers, like so:

1
rand.Seed(time.Now().UnixNano())
2
3
// random int between 0 and 100
4
num := rand.IntN(100)

JSON

Go has a builtin JSON library, it’s pretty straightforward to use. It consists of defining the JSON properties of the struct you’d like to parse and then parsing it using a decoder

For example:

1
import (
2
"encoding/json"
3
"fmt"
4
"strings"
5
)
6
7
type MyData struct {
8
Name string `json:"name"`
9
Age int `json:"age"`
10
}
11
12
func main() {
13
data := strings.NewReader("{\"name\": \"Bob\", \"age\": 25 }")
14
15
// decoder needs an io.Reader
16
decoder := json.NewDecoder(data)
17
18
var output MyData
19
err := decoder.Decode(&output)
20
21
if err != nil {
22
panic(err)
23
}
24
25
fmt.Printf("Decoded JSON: %+v", output)
26
// Decoded JSON: {Name:Bob Age:25}
27
}