Go

Installation (Win)

Build and run

cd helloworld
go build
helloworld

Exclude source file from build

Add this at the top of the .go file

// +build ignore

Go get

To fetch a package from GitHub and save it in the folder targetted by the variable GOPATH:

go get github.com/gorilla/mux

If GOPATH isn't set, it will be saved to ~\go

Test

e.g. the tests for “strings.go” live in the “strings_test.go” file. Each test is a function in the form

func TestNameOfTest(t *testing.T) { }

“t” provides helpers like t.Error and t.Fatal. They can be ran using the command

go test
  • go test always executes from the package's source directory, this makes it simple to include fixtures for your tests.
  • The go tool ignores any directory called testdata, or starts with a . or _.

Test coverage

go test -coverprofile=cover.out
go tool cover -html=cover.out

For these tools to work, you need to make sure that your code is in a subfolder of the path described in the variable GOPATH. e.g, run this before running them (Windows) :

set GOPATH=D:\CFR\Documents\Projects\CloudTrust\Go_training\introduction-to-go-master

Variable

Declaration

Those are identical

var x = 0
var x int = 0
var x int
x := 0

Assignment

var x = 1
var y = "two"
var z = 3.0

equivalent to any of the following:

var x, y, z = 1, "two", 3.0
x, y, z := 1, "two", 3.0
x, y, z := int(1), "two", float64(3.0)

Type conversion

var x uint = 700
var y int = int(x)

Functions & methods

Whenever you pass a value to a function or method, the value is copied.

Functions

func f(i int) {}
func g(i int, j int, k string) int {}
func h(i, j int) (int, int, string) { // i and j are int
  return j, i, "foobar"
}
func main() {
  a, b, c := h(1, 2)
  d, _, e := h(3, 4)
}

Methods

You can attach a function to a type that you declare : It's called a method.

type Point struct { X, Y int }
func (p Point) String() string {
         return fmt.Sprintf("point: x=%d, y=%d", p.X, p.Y)
}
func (p *Point) Move(x, y int) {
    p.X += x
    p.Y += y
}
  • In this case, calling “.String()” or “.Move(…)” on an instance of Point will run one of those methods.
  • The * in Move allows you to modify the object. (it's a pointer)
  • Note that String() is a special method that's the equivalent of toString() in Java.

Flow

For

for i := 1; i < 11; i++ {}

Switch

switch i {
case 1:
  fmt.Println("The", i, "st prime is", p)
case 2:
  fmt.Println("The", i, "nd prime is", p)
default:
  fmt.Println("The", i, "th prime is", p)
}

Remark: No “Break” needed.

Data structures

Slice

s := make([]int, 20)                  // i is of type []int
fmt.Println(len(s))                   // 20
s[7] = 20
x := s[8]                             // 0
primes := []int{2, 3, 5, 7, 11}
primes = append(primes, 13)
primes = append(primes, 17, 19, 23)
subslice1 := primes[0:3]               // 2, 3, 5   (Note: Copy by reference, not by value)
subslice2 := primes[6:]                // 17, 19, 23
for index, value := range primes {
  fmt.Println("Index:", index, "Value:", value)
}

Map

m := make(map[int]string)             // m is of type map[int]string
m[1] = "Monday"
m[2] = "Tuesday"
x := m[3]                             // x=""
y, found1 := m[3]                     // y="", found1=false
z, found2 := m[1]                     // z="Monday", found2=true
n := map[int]string{
  1: "Monday",
  2: "Tuesday",
}
delete(n, 2)                          // Remove Tuesday from n
for key, value := range n {
  fmt.Println("Key:", key, "Value:", value)
}

Struct

Definition

type Point struct {
  X int
  Y int
}
type Point struct { X, Y int }

Instantiation

var p Point
p.X = 300
p.Y = 600
p := Point{X: 300, Y: 60}

Default format

fmt.Printf("%v", p)  // {200 300}

Time

Now

now := time.Now()

Durations

start := time.Now()
// ...
elapsed := time.Since(start)

Strings

cities := []string{"A", "B", "C"}
strings.Join(cities, ", ")         // A, B, C

Formatted output

name := "John"
age := 32
fmt.Printf("Hello %s, you're %d years old", name, age)
  • %v is a magic verb that “knows how to print any value”.
  • %#v prints more information.
  • %T prints the type of the variable

Encode JSON

type Person struct {
    Name    string `json:"name"`
    City    string `json:"city,omitempty"`
    Country string `json:"-"`
    Age     int64  `json:"age,string"`
}

func main() {
    p := Person{...}
    var b bytes.Buffer
    enc := json.NewEncoder(&b)
    err := enc.Encode(p)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(b.String())
}

In this example :

  • We made sure that the JSON keys are lowercase.
  • We ommit the “City” field if its value is empty.
  • We ignore the field “Country”.
  • We convert the field “Age” to a string.

Interfaces

Go does not have an implements keyword, any type with the correct set of methods is an implementation of the interface.

Reader

type Reader interface { 
         Read(buf []byte) (int, error) 
}

Error

type error interface {
        Error() string
}

Go uses the error interface and a simple convention to implement error handling.

  • If no error occurs, the err value returned from a function or method will equal nil.
  • If an error occurs, the err value returned from a function or method will not equal nil.

Streams

Readers

File -> Reader

r, err := os.Open(path)
if err != nil {
  log.Fatal(err)
}
defer r.Close()

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

String -> Reader

const someString= `hello
world`
r := strings.NewReader(someString)

Pipe -> Reader

os.Stdin

Exemple of use :

cat some_text.txt | ./your_go_app

Concatenate multiple readers sequentially

c := io.MultiReader(a, b)

Reader that stops after n bytes

r2 := io.LimitReader(r1, int64(n))

Copy reader to writer

This example copies the Body of an HTTP response to the console.

io.Copy(os.Stdout, resp.Body)

Scanners

Reader -> Scanner

sc := bufio.NewScanner(r)

Consume from a scanner

for sc.Scan() {
  // Do something with sc.text()
}
if sc.Err() != nil {
  log.Fatal(sc.Err())
}

Buffers

Copy reader to buffer

var w bytes.Buffer
io.Copy(&w, r)

Command line arguments

args := os.Args[1:]

Files and folders

Merge filename and path

import (
  ...
  "path/filepath"
)
...
path := filepath.Join(dir, file)

Folder crawling

func CrawlDir(dir string) {
  d, err := os.Open(dir)
  if err != nil {
    log.Fatal(err)
  }
  defer d.Close()
  entries, err := d.Readdir(-1)
  if err != nil {
    log.Fatal(err)
  }
  for _, e := range entries {
    if e.IsDir() {
      // do something to dir
    } else {
      // do something to file
    }
  }
}

HTTP

Client

This gets the response of an HTTP GET.

resp, err := http.Get("http://example.com/")
if err != nil {
  // handle error
}
defer resp.Body.Close()

Then we can print it to the console…

io.Copy(os.Stdout, resp.Body) // resp.Body is a Reader

… or decode it if it's JSON.

result := make(map[string]string)
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&result); err != nil {
  // handle error
}
// e.g., result["origin"]

Server (Standard router)

import (
    "fmt"
    "net/http"
)
func index(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "This is the index page")
}
func main() {
    r := http.NewServeMux()
    r.HandleFunc("/", index)
    log.Fatal(http.ListenAndServe(":8000", r))
}

Server (Gorilla Mux)

import (
    "fmt"
    "net/http"
    "github.com/gorilla/mux"
)
func greet(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    name, ok := vars["name"]
    if !ok {
      http.Error(w, "nobody specified", 404)
    }
    fmt.Fprintf(w, "Hello %s", name)
    // To return an error back to the client: 
    // http.Error(w, err.Error(), 500)
}
func main() {
    r := mux.NewRouter()
    r.HandleFunc("/greet/{name}", greet)
    log.Fatal(http.ListenAndServe(":8000", r))
}
Print/export