About Go

Irene MulonduIrene Mulondu
13 min read

Ever since I encountered Golang, it was love at first code, its not only simple to write but also it compiles fast and even it has concurrency inbuilt in it.

Let’s dive deeper into it!

Go is an open source programming language designed for building simple, fast and reliable software.

Why Create GO

So Go was born around November 2009, a time when we already had pre-existing stable programming languages like Python, R, C, Javascript among others. Why was it born? Why did the Go authors see the need of a new language?

Why create Go.png

The main reason that sets Go apart from other languages include:

  1. It has a strong,static type system. Statically typed is a programming language characteristic in which variable types are explicitly declared and thus are determined at compile time. The main advantage of statically typed languages is Protection from Runtime Errors.

  2. The syntax is C inspired whereby it is aimed at keeping code concise and readable. This is not only for the programmer but to ease compilation.

  3. Go is network aware and concurrent. Concurrency is a program’s ability to do multiple things at the same time that is, two or more tasks that run independently of each other, at about the same time, but remain part of the same program. Python and C were created before concurrency and with the current machines being able to implement it, concurrency is patched into them but Go has been created with this in mind.

  4. It is multi-paradigm, that is both procedural and object oriented. This allows you to look at your use case and apply either that's best.

  5. Backward compatibility - this ensures that as the language grows in future one does not have to migrate

Go Playground

If you would want to test go code online, you can use go playground, which is a web service that runs on go.dev's servers. The service receives a Go program, vets, compiles, links, and runs the program inside a sandbox, then returns the output.

Installing Go

To install go, just follow the directions that I have outlined on my post on the Installing Go.

How to run Go

First off, you can use any code editor or IDE of your choice to write Go code. The one mostly used is Visual Studio Code.

An example of hello world in go:

package main

import "fmt"

func main () {
    fmt.Println("Hello World!")
}

Save the file as main.go

To run the above:

$ go run main.go

Let's unpack what we have done above:

a). package main is the declaration of the main package that will run

  • A package is a way to group functions, and it is made up of all files in the same directory

b). import fmt The import keyword is how to include code from other packages to use within our program

  • The fmt package contains functions for formatting text including printing to the console

c). func main () {...} Implement a main function to print the message to the console.

  • A main function executes by default when you run the main package.

Variables

A variable is a storage location, with a specific type and associated name. To declare a variable:

package main

import "fmt"

func main() {

// var name type
var i int
var s string

//initialize or assign values
i = 10
s = "Canada"

fmt.Println(i)
fmt.Println(s)
}

Declaration with initialization

You can declare variables and assign values on the same line:

package main

import "fmt"

func main() {

var i int = 10
var s strint = "Canada"

fmt.Println(i)
fmt.Println(s)
}

Declaration with type inference

You can omit the variable type from the declaration, when you are assigning a value to a variable at the time of declaration. The type of the value assigned to the variable will be used as the type of that variable.

package main

import "fmt"

func main() {

var i = 10
var s = "Canada"

fmt.Println(reflect.TypeOf(i)) //reflect.TypeOf returns the type of the value
fmt.Println(reflect.TypeOf(s))
}

Declaration of multiple variables

You can assign values to multiple variables in one line:

package main

import (
    "fmt"
)

func main() {
    var fname, lname string = "John", "Doe"
    var m, n, o = 1, 2, 3
    var item, price = "Mobile", 2000

    fmt.Println(fname + " " + lname) // you can use + for string concatenation(to combine several strings)
    fmt.Println(m + n + o) // returns 6
    fmt.Println(item, "-", price) // returns `Mobile - 2000`
}

Short variable declaration :=

There is no need to use the var keyword to declare the variable type.

package main

import (
    "fmt"
    "reflect"
)

func main() {
    name := "John Doe"
    fmt.Println(reflect.TypeOf(name))
}

Zero Values

If you declare a variable without assigning it a value, it will automatically bind a default value or a zero value :

package main

import "fmt"

func main() {
    var quantity float32 // zero value of float is 0
    fmt.Println(quantity)

    var price int16 // zero value of int is 0
    fmt.Println(price)

    var product string // zero value of string is "", an empty string
    fmt.Println(product)

    var inStock bool // zero value of bool is false
    fmt.Println(inStock)
}

Scope of Variables

Golang uses lexical scoping based on code blocks to determine the scope of variables. Inner block can access its outer block defined variables, but outer block cannot access its inner block defined variables.

package main

import (
    "fmt"
)

var s = "Japan"

func main() {
    fmt.Println(s)
    x := true

    if x {
        y := 1
        if x != false {
            fmt.Println(s)
            fmt.Println(x)
            fmt.Println(y)
        }
    }
    fmt.Println(x)
}

Note: Short variable declaration is allowed only for declaring local variables, that is, variables declared within the function. When you declare variables outside the function, you must do so using the var keyword.

Data types

Below are some GO basic data types:

  1. Numeric type

a) int: Signed integer types (int8, int16, int32, int64)

var a int = 5

b) Floating-point types: float32, float64

var z float64 = 3.14
  1. String

    These are sequences of characters

     var str string = "Welcome to Go!"
    
  2. Boolean

    This represents true or false

     var isRight bool = true
    
  3. Array

    This is a fixed collection of elements of the same type. You need to define the size. Like below we have an array of 3 strings.

     var strArr [3]string = [3]string{"Jane", "John", "Jill"}
    
  4. Slice

    This is a resizable collection built on top of arrays. You do not have to define the size.

     var strArr []string = []string{"Jane", "John", "Jill"}
    
  5. Map

    This is unordered collection of key-value pairs:

     var m map[string]int = map[string]int{"one": 1, "two": 2, "three": 3}
    
  6. Struct

    This is a composite data type that groups together variables as fields under a single name:

     type User struct {
         Name string
         Age  int
     }
    
     var user1 User = User{"Jil", 23}
    

Functions

In Go, functions are blocks of code that perform specific tasks, which can be reused throughout the program to save memory, improve readability, and save time. Functions may or may not return a value to the caller.

// multiply() multiplies two integers and returns the result
func multiply(a, b int) int {
    return a * b
}

result = multiply(5,10)

Types

Type is the base interface for all data types in Go. All data types (int, float, string, bool) implement the Type interface. You can also create your own types.

The following is the Type interface:

type Type interface {
    // Underlying returns the underlying type of a type.
    Underlying() Type

    // String returns a string representation of a type.
    String() string
}

Methods

Go does not have classes. However, you can define methods on types.

A method is a function with a special receiver argument.

The receiver appears in its own argument list between the func keyword and the method name.

In this example, the Abs method has a receiver of type Vertex named v.

package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 {

    return math.Sqrt(v.X + v.Y)
}

func main() {
    v := Vertex{3, 4}
    fmt.Println(v.Abs())
}

Interfaces

An interface type is a set of method signatures.

package main

import (
    "fmt"
    "math"
)

type geometry interface { // Here’s a basic interface for geometric shapes.
    area() float64
}

type rect struct { // we’ll implement this interface on rect type.
    width, height float64
}

func (r rect) area() float64 { // here we implement the method in the interface
    return r.width * r.height
}

func measure(g geometry) { // calling the methods in the geometry interface
    fmt.Println(g)
    fmt.Println(g.area())
}

func main() {
    r := rect{width: 3, height: 4}

// The circle struct type implements the geometry interface, so we can use the instance 
// of this struct as arguments to measure.
    measure(r)
}

Conditional Statements

In Go, conditional statements allow the execution of specific code blocks based on conditions.

They control the flow of execution in Go programs.

Main conditional statements in Go are:

1. if Statement

  • Used to execute code if a condition is true.
package main
import "fmt"

func main() {
    age := 20
    if age >= 18 {
        fmt.Println("You are an adult.")
    }
}

2. if-else Statement

  • Provides an alternative block of code if the condition is false.
package main
import "fmt"

func main() {
    age := 16
    if age >= 18 {
        fmt.Println("You are an adult.")
    } else {
        fmt.Println("You are a minor.")
    }
}

3. if-else if-else Statement

  • Used when multiple conditions need to be checked.
package main
import "fmt"

func main() {
    score := 85
    if score >= 90 {
        fmt.Println("Grade: A")
    } else if score >= 75 {
        fmt.Println("Grade: B")
    } else {
        fmt.Println("Grade: C")
    }
}

4. Switch Statement

  • A cleaner way to check multiple conditions.
package main
import "fmt"

func main() {
    day := "Monday"
    switch day {
    case "Monday":
        fmt.Println("Start of the week!")
    case "Friday":
        fmt.Println("Weekend is coming!")
    default:
        fmt.Println("A regular day.")
    }
}

Loops

Loops in Go

In Go, loops are used to execute a block of code multiple times. Unlike other languages, Go has only one looping construct: for loop, which can be used in different ways.

1. Basic for Loop

  • Similar to a for loop in C, Java, or Python.

  • It consists of three parts: initialization, condition, and increment/decrement.

package main
import "fmt"

func main() {
    for i := 1; i <= 5; i++ {
        fmt.Println("Iteration:", i)
    }
}

2. for as a While Loop

  • If you omit the initialization and increment, for behaves like a while loop.
package main
import "fmt"

func main() {
    i := 1
    for i <= 5 {  // Works like while(i <= 5)
        fmt.Println("Iteration:", i)
        i++
    }
}

3. Infinite for Loop

  • If you omit all three parts, Go runs an infinite loop.
package main
import "fmt"

func main() {
    for {
        fmt.Println("This will run forever!")
        break // Use break to exit, or it runs infinitely
    }
}

4. for Loop with range (Iterating Over Arrays/Slices/Maps/Strings)

  • The range keyword allows iterating over collections like arrays, slices, maps, strings and channels

a). range over Maps

  • When iterating over a map, range returns both the key and value.

  • The order of iteration is not guaranteed because Go maps are unordered.

package main
import "fmt"

func main() {
    studentGrades := map[string]int{"Alice": 90, "Bob": 85, "Charlie": 92}

    for key, value := range studentGrades {
        fmt.Println("Name:", key, "Grade:", value)
    }
}
  • If you only need the keys, you can ignore the values:

      key := range studentGrades {
          fmt.Println("Student:", key)
      }
    
  • If you only need the values, you can ignore the keys using _:

      _, value := range studentGrades {
          fmt.Println("Student:", value)
      }
    

b). range Over Slices

  • When iterating over a slice, range returns both the index and the value.
package main
import "fmt"

func main() {
    numbers := []int{10, 20, 30, 40}

    for index, value := range numbers {
        fmt.Println("Index:", index, "Value:", value)
    }
}
  • If you only need values (not the index), use _:

      for _, value := range numbers {
          fmt.Println("Value:", value)
      }
    
    • If you only need index:

    •       for index := range numbers {
                fmt.Println("index:", index)
            }
      

c). range Over Channels

  • range can be used to receive values from a channel until it's closed.
package main
import "fmt"

func main() {
    ch := make(chan int)

    // Sending values to channel in a separate goroutine
    go func() {
        for i := 1; i <= 3; i++ {
            ch <- i
        }
        close(ch) // Close the channel to stop range iteration
    }()

    // Receiving values using range
    for val := range ch {
        fmt.Println("Received:", val)
    }
}

Key Notes on Channels:

  • The loop stops automatically when the channel is closed.

  • If the channel is not closed, the loop will block (wait forever).

5. Using break and continue

  • break: Exits the loop.

  • continue: Skips the current iteration and moves to the next one.

package main
import "fmt"

func main() {
    for i := 1; i <= 5; i++ {
        if i == 3 {
            continue // Skips 3
        }
        fmt.Println("Value:", i)
    }
}

Concurrency in Go (Goroutines & Channels)

Go provides built-in concurrency support using goroutines and channels, making it easy to write efficient concurrent programs.

1. Goroutines (Lightweight Threads)

  • A goroutine is a lightweight thread managed by the Go runtime.

  • Use the go keyword before a function to run it as a goroutine.

  • Goroutines execute asynchronously, meaning the main function won't wait for them to complete unless explicitly told.

      package main
    
      import (
          "fmt"
          "time"
      )
    
      func printMessage(message string) {
          for i := 1; i <= 5; i++ {
              fmt.Println(message, i)
              time.Sleep(time.Millisecond * 500) // Simulate work
          }
      }
    
      func main() {
          go printMessage("Hello from Goroutine") // Start goroutine
          printMessage("Hello from Main")         // Main function executes normally
      }
    

    2. Channels (Communication Between Goroutines)

    • Channels provide safe communication between goroutines.

    • Use chan to define a channel.

    • Sending and receiving data using <- ensures goroutines work together.

package main
import "fmt"

func sendData(ch chan string) {
    ch <- "Hello from Goroutine" // Send message to channel
}

func main() {
    ch := make(chan string) // Create a channel

    go sendData(ch) // Start goroutine

    message := <-ch // Receive data from channel
    fmt.Println(message)
}

3. Buffered Channels

  • A buffered channel allows sending multiple values without blocking until the buffer is full.
package main
import "fmt"

func main() {
    ch := make(chan string, 2) // Buffered channel with size 2

    ch <- "Message 1"
    ch <- "Message 2"

    fmt.Println(<-ch) // Receive first message
    fmt.Println(<-ch) // Receive second message
}

4. select Statement (Handling Multiple Channels)

  • select lets you listen to multiple channels and process whichever one is ready first.
package main
import (
    "fmt"
    "time"
)

func sendMessages(ch1, ch2 chan string) {
    time.Sleep(time.Second)
    ch1 <- "Message from ch1"
    ch2 <- "Message from ch2"
}

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go sendMessages(ch1, ch2)

    select {
    case msg := <-ch1:
        fmt.Println("Received:", msg)
    case msg := <-ch2:
        fmt.Println("Received:", msg)
    }
}

5. WaitGroups (Syncing Goroutines)

  • sync.WaitGroup ensures the main function waits for goroutines to finish.
package main
import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // Decrement counter when done
    fmt.Println("Worker", id, "started")
    time.Sleep(time.Second)
    fmt.Println("Worker", id, "finished")
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 3; i++ {
        wg.Add(1) // Increment counter
        go worker(i, &wg)
    }

    wg.Wait() // Wait for all goroutines to complete
    fmt.Println("All workers finished")
}

References

  1. A tour of Go

  2. Go by Example

  3. Go Playground

  4. Geeks for geeks Go Tutorial

0
Subscribe to my newsletter

Read articles from Irene Mulondu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Irene Mulondu
Irene Mulondu

I am a GIS enthusiast. Mostly what I do is make maps, create and manage GIS softwares and I Iove R and am learning to appreciate Python. So mainly am a learner in GIS and programming and I like to explore integration of various programming algorithms with GIS.