Notes on Go

Repeating the same argument to Printf

If we wanted to repeat the same argument to a call to fmt.Printf(), we can make use of “indexed” arguments. That is, instead of writing fmt.Printf("%s %s", "Hello", "Hello"), we can write fmt.Printf("%[1]s %[1]s", "Hello"). Learn about it in the docs.

Multi-line strings

Things are hassle free on the multi-line strings front:

package main

import (
	"fmt"
)

func main() {

	s := `Multi line strings
are easy. So are strings with "double quotes"
and 'single quotes'`
	fmt.Print(s)

}

Maps with values as maps

First, we define the map:

var clusters = make(map[int]map[string]int)

Then, we assign a value which is another map, that we create:

clusters[clusterNum] = map[string]int{
				"a": 1,
				"b": 1,
}

We can then modify the map defined as the value, like so:

clusters[1]["a"] = 5

Check if a key is present in a map

Here we can make use of the multiple return value from the map query statement:

// The ok variable will be true if key present
// else false
if _, ok := flatMap[tblName]; !ok {
     // not present
} else {
    // present
}

Reading data from a database into structs

The following snippet can be used to read the rows of a table from a SQL db into structure variables:

package main

import "fmt"

type Node struct {
	data1 string
	data2 string
}

func main() {

	//..

	rows, err := db.Query(q)
	var results []Node

	if err != nil {
		fmt.Printf("Error querying: %v\n", err.Error())
	} else {
		defer rows.Close()

		for rows.Next() {
			var n Node
			err = rows.Scan(&n.data1, &n.data2)
			if err != nil {
				fmt.Printf("Error serializing data into variable: %v\n", err)
			}
			results = append(results, n)
		}
	}
}

Check if a string starts with another string

// Does the value in `variable` start with "prefix"
strings.HasPrefix(variable, "prefix")

Genearate a random integer in [0,n)

fmt.Printf("Random integer between 0-100: %v\n", rand.Intn(100))

Quotient and Modulus

fmt.Printf("1/2 - Modulus: %v Quotient: %v", 1%2, 1/2)

Walking a directory tree

Let’s say we want to walk a directory tree and only list/perform operation on files of a certain extension:

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

func main() {

	// Walk the directory tree starting at os.Args[1]
	err := filepath.Walk(os.Args[1], func(path string, info os.FileInfo, err error) error {
		if err != nil {
			panic(err)
		}
		if info.IsDir() {
			return nil
		}
		if filepath.Ext(path) == ".go" {
			// Extract just the "filename" part of "filename.go"
			fName := filepath.Base(path)
			filename := strings.TrimSuffix(fName, filepath.Ext(path))
			fmt.Printf("path: %s filename: %s\n", path, filename)

		}
		return nil
	})
	if err != nil {
		panic(err)
	}

}

Reading structure tags

package main

import (
	"fmt"
	"reflect"
)

type node struct {
	Rule string `metadata1:"value1" metadata2:"value2"`
	Name string `metadata2:"value3"`
}

func main() {

	n := node{
		Rule: "A rule",
		Name: "A name",
	}
	// Reflect example  code from
	// https://gist.github.com/drewolson/4771479
	val := reflect.ValueOf(&n).Elem()
	for i := 0; i < val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)
		tag := typeField.Tag
		metadataOneAttribute := tag.Get("metadata1")
		metadataTwoAttribute := tag.Get("metadata2")

		fmt.Printf("%v (metadata1: %v metadata2: %v)\n", valueField.Interface(), metadataOneAttribute, metadataTwoAttribute)

	}
}

Dot imports in Go

When you import a package as import . "path/to/pkg", you don’t have to use pkg.ExportedSymbol to refer to the ExportedSymbol in pkg any more. You can just use ExportedSymbol.

Learn more about it here. Of course, it is not a feature that’s well liked by everyone in the community and there is a proposal to remove it from Go 2.

I first learned about it in this podcast.