Go by example

Table of Contents:

Here I created a list of Go examples for fast dive in:

To print a line use Println from fmt.

package main

import "fmt"

func main() {
    fmt.Println("Hello Go")
}

Explanation of the fist Go program:

Every Go program is made up of packages and the Go program start running in package main.

From the package main another package fmt is imported.

The package fmt is defined somewhere and it starts with:

package fmt

Comments

Multiline

/*
comments 
here
*/

Inline

// comments here

Import Packages

Every Go program is made up of packages. You can import multiple packages line by line:

import "fmt"
import "math"

or from once:

import (
   "fmt"
   "math"
)

Define variables

var a = "apple"
var b, c int = 1, 2
var d = true
var e int
f := "apple"

The := syntax will declare and initialize a variable, equivalent to:

var f string = "apple"

It is possible to use short notation for multiple variables:

golang, python, sql := true, false, "no!"

Variables declared without an explicit initial value are by default set to zero:

  • 0 for numeric types,
  • false for the boolean type, and
  • "" (the empty string) for strings.

Exported variables

There is a simple rule in Go. A name is exported if it begins with a capital letter. For example, Pi is exported from the math package.

After you import a package, you can refer only to package exported names.

Constants

Just like variables but defined with the const keyword.

Constants cannot use the := syntax.

Example:

const Pi = 3.14

Types

The following types are very commonly used:

bool
string
int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64
complex64 complex128
byte // same as uint8
rune // same as int32

The int, uint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems.

Reflect types

Example: get a type

package main

import (
    "fmt"
    "reflect"
)

func main() {

    o1 := "string"
    o2 := 10
    o3 := 1.2

    fmt.Println(reflect.TypeOf(o1))
    fmt.Println(reflect.TypeOf(o2))
    fmt.Println(reflect.TypeOf(o3))

}

Out:

string
int
float64

Convert values

In Go assignment between items of different type must be explicit.

Example:

var i int = 42
var f float64 = float64(i)

Looping

In go you create forever loop using for:

for {}

You create while loop using for:

i:=1
for i<10 {
   i++
}

You create for loop:

for i := 0; i < 10; i++ {
   // something
}

where there are three parts:

  • first part is the the init statement executed before the first iteration
  • the condition expression is evaluated before every iteration
  • the post statement is executed at the end of every iteration

Branching statements

Example:

var i := 10

if i%2 == 0 { 
   // if i is even
}


var i := 11
if i%2 == 0 { 
   // if i is even
} else{
   // else i is odd
}

var i := -2
switch i {
    case 1:
        fmt.Println("one")
    case 2:
        fmt.Println("two")
    case 3:
        fmt.Println("three")
    }
    default:
        fmt.Println("something else")

Arrays, slices and maps

This is an array of 10 elements of type int:

var a [10]int

You can print it:

package main

import "fmt"

func main() {
   var a [10]int
   fmt.Println(a)
}

Out:

[0 0 0 0 0 0 0 0 0 0]

By default arrays are initialized as zeros.

A slice, is flexible view into the elements of an array.

package main

import "fmt"

func main() {
   primes := [6]int{1, 2, 3, 4, 5, 6}

   var s []int = primes[1:4]
   fmt.Println(s)
}

Out:

[2 3 4]

Maps are built-in associative data type (also called hashes or dicts in other languages).

Example:

package main
import "fmt"
func main() {

   m := make(map[string]int)

   m["k1"] = 7
   m["k2"] = 13

   fmt.Println("map:", m)

   v1 := m["k1"]
   fmt.Println("v1: ", v1)

   fmt.Println("len:", len(m))

Out:

map: map[k1:7 k2:13]
v1:  7
len: 2

Sorting arrays

Example:

package main

import (
  "fmt"
  "sort"
)

func main() {
  numbers := []int{1, 11, -5, 7, -2, 0, 12}
  sort.Ints(numbers)
  fmt.Println("Sorted:", numbers)
}

Output:

Sorted: [-5 -2 0 1 7 11 12]

Functions

A function can take zero or more arguments.

func add(x int, y int) int {
   return x + y
}

This function returns int and it takes two parameters of type int.

Notice: that the type comes after the variable name.

When two or more consecutive named function parameters share a type, there is a short notation.

x int, y int

Shorter notation:

x, y int

A function can return multiple results:

func addsub(x , y int) (int, int) {
   return x + y, x-y
}

Closures

Go supports anonymous functions, which are used to create losures.

Example: Anonymous function

func(parameter_list)(return_type){
// code..
return // can be omitted
}()

A closure uses anonymous function to reference outside variables.

Example: Closure

package main

import "fmt"

func getLimit() func() int {
    limit := 100
    return func() int {
        limit -= 1
        return limit
    }
}

func main() {
    limit := getLimit()
    fmt.Println(limit())
    fmt.Println(limit())
}

Output:

99
98

String builder

Example using the for loop:

How to create a big string from all the numbers less than 1000.

package main

import (
    "strings"
    "fmt"
    "strconv"
)

func main() {
    var sb strings.Builder

    for i := 0; i < 1000; i++ {
        sb.WriteString(strconv.Itoa(i))
    }

    fmt.Println(sb.String())
}

Concurrency

Defer

Go is designed at Google with CSP-style concurrency in mind (Communicating Sequential Processes).

Much of Go success is thanks to the defer statement.

When you call a function you can have a defer part.

func main() {
   defer fmt.Println("world")
   fmt.Println("hello")
}

This will print:

hello
world

You can stack defer statements:

func main() {
   fmt.Println("start")
   for i := 0; i < 10; i++ {
      defer fmt.Println(i)
   fmt.Println("end")
}

Out:

start
end
9
8
7
6
5
4
3
2

Goroutines

Another Go trump is goroutine achieved with the go keyword.

Goroutine is a lightweight thread managed by the Go runtime.

Whenever you call go with some func name a new goroutine will be created.

package main

import (
   "fmt"
   "time"
)

func fnc(s string) {
   for i := 0; i < 5; i++ {
      time.Sleep(100 * time.Millisecond)
      fmt.Println(s)
   }
}

func main() {
   go fnc("world")
   fnc("hello")
}

Channels

Channels are the pipes that connect concurrent goroutines.

Examples:

int_ch := make(chan int)
messages_ch := make(chan string)

First channel is int and the second is string.

You send values into channels from one goroutine and receive those values into another goroutine.

go func() { messages_ch <- "abc" }()
msg := <- messages_ch // get the message
fmt.Println(msg) // print the message

When you create a channel you set the max number of elements it can have:

int_ch := make(chan int, 2)

Now, channel int_ch cannot take more than 2 elements.

You can close a channel with close command:

close(int_ch)

Select (await)

Using select you can wait on multiple channel operations.

package main

import (
   "fmt"
   "time"
)

func main() {

   c1 := make(chan string, 2)
   c2 := make(chan string)

   
   go func() {
      time.Sleep(1 * time.Second)
      c1 <- "one"
      time.Sleep(1 * time.Second)
      c1 <- "one"
      
   }()
   go func() {
      time.Sleep(2 * time.Second)
      c2 <- "two"
   }()

   for i := 0; i < 3; i++ {
      // select is awaiting
      select {
      
      case msg1 := <-c1:
         fmt.Println("received", msg1)

      case msg2 := <-c2:
         fmt.Println("received", msg2)

Output:

received one
received one
received two

tags: examples & category: golang