Skip to content

Instantly share code, notes, and snippets.

@shaunhess
Last active February 11, 2022 03:07
Show Gist options
  • Save shaunhess/7615761 to your computer and use it in GitHub Desktop.
Save shaunhess/7615761 to your computer and use it in GitHub Desktop.
Go - Quick Reference Guide

golang - Quick Reference Guide

Advantages of Go

  1. Garbage Collection - Automatic memory management
  2. Fast to compile and execute
  3. Simpler Langauge
  4. Concurrency is efficient

Get started by downloading Go @ https://golang.org/dl/

Useful Links

Go By Example

Linux Platform (example)

wget https://go.dev/dl/go1.17.6.linux-amd64.tar.gz
sudo tar -xvf go1.17.6.linux-amd64.tar.gz
sudo mv go /usr/local
vi ~/.bashrc
# Add the following to your .bashrc:
# export GOROOT=/usr/local/go
# export GOPATH=$HOME/dev/go
# export PATH=$GOPATH/bin:$GOROOT/bin:$PATH

Mac Platform (example)

mkdir -p /Users/<username>/Development/go/src
vi ~/.bashrc
# Add the following to your .bashrc:
# export GOROOT=//Users/<username>/Development/go
# export GOPATH=/Users/<username>/Development/go
# export PATH=$PATH:/Users/<username>/Development/go/bin
vi ~/.bash_profile
if [ -f ~/.bashrc ]; then . ~/.bashrc; fi

Useful go cmds

cmd Example Description
go run compile and run the program compile and run the program
go build compiles the program compiles the program
go mod formats source code files formats source code files
go fmt formats source code files formats source code files
go doc go doc fmt prints documentation for pkg
go test run test files run test files
go get downloads pkgs and installs them downloads pkgs and installs them
go list list installed pkgs list installed pkgs

Get additional tools

go get golang.org/x/tools/cmd/goimports

Get Help

# godoc pkgname
godoc fmt
# godoc pkgname method
godoc fmt Println
# go docs locally
godoc -http=:8000

Workspace - Common organization of hierarchy of directories (GOPATH)

  • 3 Common directories -- src - Contains source code files -- pkg - Contains packages (libraries) -- bin - contains compiled executables

First Go Program

Save the following to github.com<username>\hello\hello.go

package main

import "fmt" # used to access other packages

func main () {
    fmt.Printf("Hello World\n")
}

Execute the file go run hello.go

Build the file to an executable go build hello.go

Build and install the program Note that you can run this command from anywhere on your system. The go tool finds the source code by looking for the github.com/user/hello package inside the workspace specified by GOPATH or GOROOT. go install github.com\<username>\hello hello

If you're using a source control system, now would be a good time to initialize a repository, add the files, and commit your first change.

First Go Library

New-Item -type Directory \src\github.com\<username>\newmath"
# Save the following to github.com\<username>\newmath\sqrt.go
# Package newmath is a trivial example package
package newmath # package declaration to identify what package the file belongs too

# Sqrt returns an approximation to the square root of x
func Sqrt(x float64) float64 {
    z := 1.0
    for i := 0; i < 1000; i++ {
        z -= (z*z - x) / (2 * z)
    }
    return z
}

go build github.com\<username>\newmath you can now run: import "github.com/user/newmath" in your programs

First Go Test

Save the following to github.com<username>\newmath\sqrt_test.go

package newmath

import "testing" // import external package

func TestSqrt(t *testing.T) {
	const in, out = 4, 2
	if x := Sqrt(in); x != out {
		t.Errorf("Sqrt(%v) = %v, want %v", in, x, out)
	}
}

go test github.com\<username>\newmath

Truth Table

Expression Value
true && true true
true && false false
false && true false
false && false false
`true
`true
`false
`false
!true false
!false true

Printf verbs

%d // decimal integer %x, %o, %b // integer in hex, octal, binary %f, %g, %e // float; 3.141593, 3.141592653589793, 3.41593e+00 %t // bool; true or false %c // rune (Unicode code point) %s // string %q // quoted string "abc" or rune 'c' %v // any value in a natural format %T // type of value %% // literal percent sign (no operand)

//******************************************************************************* // Variables - Data stored in memory // Store or hold a value // case sensitive; must begin with a letter' can't use keywords // keyword name type = value //******************************************************************************* var x string = "Hello World" // sequence of bytes stored in unicode var x, y, z int = 1, 2, 3 // integer values var x float = 1.0 // fractional (decimal values) var c, python, java bool = true, false, false var b, f, s = true, 2.3, "four" // bool, float, string var f, err = os.Open(name) // returns a file handle and an error (default nil)

// group variables var ( j = 356.245 k int = 0 inter, floater, stringer = 1, 2.0, "hi" )

/* Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type. */ func main() { x, y, z := 1, 2, 3

fmt.Println(x, y, z)

}

// Using the variable to assign itself var x string x = "Hello " x += "World" // or x = x + "World"

// Try to reduce var scope and combine statements err := r.ParseForm() if err != nil { log.Print(err) }

// Now refactor too if err := r.ParseForm(); err != nil { log.Print(err) }

// Binary operators // Arithmetic: +, -, *, /, %, <<, >> // Comparison: ==, !=, >, <, >=, <= // Boolean: &&, ||

//******************************************************************************* // Variables Scope - where can a variable be accessed in code // Blocks - a sequence of declarations and statements; lexical scoping // - Explicit and Implicit Blocks // -- Universe Block - all Go source // -- Package Block - within a package // -- File Block - all source within a file // -- Matching sequence Block - if, for, switch, func //******************************************************************************* var x int = 4

func f() { fmt.Printf("%d", x) // 4 }

func g() { fmt.Printf("%d", x) // 4 }
// --------------------------- func f() { x := 4 // scoped to func f fmt.Printf("%d", x) // 4 }

func g() { fmt.Printf("%d", x) // error }

//******************************************************************************* // Pointers - address to data in memory //******************************************************************************* &x // returns the address of a variable or func *x // returns data at the address (dereferencing)

ptr := new(int) // new() func creates a var and returns a pointer to the var *ptr = 3

//******************************************************************************* // Constants // Value is known at compile time //******************************************************************************* // Constants can be character, string, boolean, or numeric values. // Constants cannot be declared using the := syntax. All files in package will // have access. const Pi = 3.14

const ( cycles = 5 res = 0.001 size = 100 )

// Generate const using iota type Grades int const ( A Grades = iota B C D F ) // A is: 0, B is: 1, C is: 2, D is: 3, F is: 4 fmt.Printf("A is: %v, B is: %v, C is: %v, D is: %v, F is: %v", A, B, C, D, F)

//******************************************************************************* // Functions //******************************************************************************* // func funcname (params) returntype; call by value - args are copied to params // Advantages: Data Encapsulation; Disadvantages: Copying time func add(x int, y int) int { return x + y } add(42, 13)

// call by reference - pass a pointer func add(x *int) { *x = *x + 1 } func main() { x := 2 add(&x) fmt.Println(x) // 3 }

// Passing an array by pointer (better to use a slice) func main() { a := [...]int{1,2,3,4,5} // in an array literal ellipsis "..." determines len by the num values printArrayContents(&a) }

// Passing an array by pointer func printArrayContents(x *[5]int) { for i, v := range *x { fmt.Printf("index: %d, value: %d\n", i, v) } }

// Passing a slice by reference func main() { a := []int{1,2,3,4,5} // slice doubleSliceContents(a) // slice passes by reference and doesn't copy the data fmt.Println(a) }

func doubleSliceContents(x []int) { for i, v := range x { x[i] = x[i] + v } }

// variadic func (...) to take variable num of arguments // args treated as a slice inside func // fmt.Println(add(1,2,3)) // or pass a slice fmt.Println(add(slicename...)) func add(args ...int) int { total := 0 for _, v := range args { total += v } return total }

// Return two strings func swap (x, y string) (string, string) { return y, x } swap("hello", "world")

// Don't care about the first string use underscore; val will equal "world" _, val = swap("hello", "world")

// Example program that has a func that aceepts input package main

import "fmt"

func main() { fmt.Print("Enter a number: ") var input float64 fmt.Scanf("%f", &input)

output := input * 2

fmt.Println(output)

}

// Receiver func func (p person) print() { fmt.Println(p) }

// Functions are First-Class and can be treated like any other type /* Variables can be declared with a func type Can be created dynamically Can be passed as arguments and returned as values Can be stores in data structures */

// func as an argument func applyIt(afunc func (int) int, val int) int { return afunc(val) }

func incFn(x int) int {return x+1} func decFn(x int) int {return x-1}

func main() { fmt.Println(applyIt(incFn, 2)) fmt.Println(applyIt(decFn, 2)) }

// Anonymous function (Lambda) func applyIt(afunc func (int) int, val int) int { return afunc(val) }

func main() { v := applyIt(func (x int) int {return x+1}, 2} fmt.Println(v) }

//******************************************************************************* // Types //******************************************************************************* /* Value Type Reference Type Need *pointer to update No pointer needed to update ---------------------------------------------------------------------- int | slices float | maps string | channels bool | pointers struct | functions */

type Celsius float64 // ~15 digits of precision type IDnum int

var temp Celsius = 32.0

// Value Type Example----------------------- type person struct { firstName string lastName string }

john := person{ firstName: "John", lastName: "Doe" }

// Receiver func, w/ pointer to a person func (p *person) updateLastName(newLastName string) { p.lastName = newLastName }

// Receiever func func (p person) print() { // print field name and value fmt.Printf("%+v\n", p) }

// Another Reciever func or method associated with type type myInt int func (mi myInt) Double() int { return int(mi*2) } func main() { v := MyInt(3) fmt.Println(v.Double()) // 6 }

john.updateLastName("Smith") john.print() // {firstName:John lastName:Smith}

// Reference Type Example------------------- mySlice := []string{"Hello"}

func updateSlice(s []string) { s[0] = "Bye" }

updateSlice(mySlice) fmt.Println(mySlice) // Bye

// Type Conversion var x int32 = 1 var y int16 = 2 x = y // error x = int32(y) // success

//******************************************************************************* // If Statement // if { // // } //******************************************************************************* if x > 0 { return y }

// Another example with a test condition if ValidateURL := strings.HasPrefix("http://www.devalex.us", "http://"); ValidateURL != true { url := "http://" + "www.devalex.us" }

// If with a variable assignment. Only in scope until the end of the if/else func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } return lim }

// FizzBuzz for i := 1; i < 100; i++ { if i % 3 == 0 && i % 5 == 0 { fmt.Println("FizzBuzz") } else if i % 3 == 0 { fmt.Println("Fizz") } else if i % 5 == 0 { fmt.Println("Buzz") } else { fmt.Println(i) } }

//******************************************************************************* // Switch Statement //******************************************************************************* switch a { case 0: fmt.Printf("0") default: fmt.Printf("non-zero") }

// or with a expression a, b := x[i], y[j] switch { case a < b: return -1 case a == b: return 0 case a > b: return 1 }

switch i { case 0: fmt.Println("Zero") case 1: fmt.Println("One") case 2: fmt.Println("Two") case 3: fmt.Println("Three") default: fmt.Println("Unknown Number") }

// Using a func func SigNum(x int) int { switch { case x > 0: return +1 case x < 0: return -1 default: return 0 } }

// Another func example func printValue(v interface{}) { switch v := v.(type) { case string: fmt.Printf("%v is a string\n", v) case int: fmt.Printf("%v is an int\n", v) default: fmt.Printf("The type of v is unknown\n") } }

//******************************************************************************* // For Loops //******************************************************************************* /* for initialization; condition; post { // initialization - executes before the loop starts; typically a var // declaration, assigment statement, or func call // condition - boolean expression; loop ends when it evals to false // post - executes after the body of the loop // zero or more statements } */ sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum)

// For is also used as a traditional while loop /* for condition { // zero or more statements } */ sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum)

//******************************************************************************* // Structs // Sequence of named elements, called fields, each of which has a name and a type // //******************************************************************************* type Vertex struct { X int Y int }

// Access struct fields w/ dot func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X)

// or
v := new(Vertex)
fmt.Println(v) // 0 0
v.X, v.Y = 11, 9
fmt.Println(v) // 11 9

// or preferred
v := Vertex{
    X = 1,
    Y = 2
}
fmt.Println(v)

}

// Another example for completness package main

import ( "fmt" )

type Employee struct { ID int Name string Position string Salary int }

func main() { var dilbert Employee dilbert.ID = 1 dilbert.Name = "Dilbert" dilbert.Salary = 50000

fmt.Println(Bonus(&dilbert, 10))
fmt.Printf("dilbert salary before raise is %d\n", dilbert.Salary)
dilbert.AwardAnnualRaise()
fmt.Printf("dilbert salary after raise is %d\n", dilbert.Salary)

}

func Bonus(e *Employee, percent int) int { return e.Salary * percent / 100 }

// Receiver func; add method to Employee struct func (e *Employee) AwardAnnualRaise() { e.Salary = e.Salary * 105 / 100 }

/* Use struct literal initialization to avoid invalid intermediate state. Inline struct declarations where possible.

foo, err := newFoo(*fooKey, fooConfig{ Bar: bar, Period: 100 * time.Millisecond, Output: nil, }) if err != nil { log.Fatal(err) } defer foo.close()

*/

//******************************************************************************* // Closures (Function Literals) Functions within Functions + its environment //******************************************************************************* func f() { for i := 0; i < 10; i++ { g := func(i int) { fmt.Printf("%d",i) } g(i) } }

//******************************************************************************* // Arrays // Fixed length sequence of 1 or more elements of a particular type. //******************************************************************************* var ar [3]int ar[0] = 1

// get length len(ar)

func f(a [3]int) { fmt.Println(a) }

f(ar) // passes a copy of the array f(&ar) // passes a pointer to the array

ar[:n] // same as ar[0:n] ar[n:] // same as ar[n:len(ar)] ar[:] // same as [0:len(ar)]

// Literal Array var x [5]int = [5]{1,2,3,4,5}

// print the indices and elements of an array a := [...]int{1,2,3} // in an array literal ellipsis "..." determines len by the num values for i, v := range a { fmt.Printf("%d, %d\n", i, v) }

months := [...]string{1: "January", 2: "February", 3: "March"}

// test for item in array func stringInSlice(a string, list []string) bool { for _, b := range list { if b == a { return true } } return false }

//******************************************************************************* // Slices // Variable length sequence of 1 or more elements of a particular type. // Window on an underlying array //******************************************************************************* var arr = [...]string{"a", "b", "c", "d", "e"} s1 := arr[1:3] // "b", "c" s2 := arr[2:5] // "c", "d", "e"

fmt.Printf("Length: %d, Capacity: %d", len(s1), cap(s1))

// Slice Literal var sliceLiteral = []int{1,2,3}

// or var sli = make([]int, 10, 15) // array of ints with len of 10 and cap of 15

// Increase size of slice and underlying array if neccessary var sli = make([]int, 0, 3) sli = append(sli, 100) // add the int 100 to append to sli slice

//******************************************************************************* // Maps // Unorder collection of key-value pairs (i.e. hash table or dict) // Keys must be the same type; Values must be the same type // Keys and Values can be of a different type //******************************************************************************* package main

import ( "fmt" )

func main() { // Create a map (hashtable) m := map[string]float64{"one": 1.0, "pi": 3.1415}

// Add a new value
m["two"] = 2.0

// Delete a value
delete(m, "one")

// Detect if a key is in the map
v, p := m["one"] // v = 0, p(resense) = false

printMap(m)

}

func printMap(m map[string]float64) { // Iterate over a map for key, value := range m { fmt.Printf("key %s, value %g\n", key, value) } }

// Test for an entry /* If x is present in the map, set value to the entry for the key and ok to a boolean of true If x is not present in the map, set value to zero of its type (0, "0", nil) and ok to a boolean of false */ value, ok := m[x]

// Delete an entry m[x] = 0, false

elements := map[string]string{ "H": "Hydrogen", "He": "Helium", "Li": "Lithium", "Be": "Beryllium", "B": "Boron", "C": "Carbon", "N": "Nitrogen", "O": "Oxygen", "F": "Fluorine", "Ne": "Neon", }

// Another Example m := make(map[string]string)

m["bandName"] = "Funny Bones" // "create" websiteTitle := m["bandName"] + " Music" // "read" m["bandName"] = "Moon Taxi" // "update" delete(m, "bandName") // "delete" fmt.Printf(m["bandName"]) // prints nothing since m["bandName"] == ""

for key, value := range m { fmt.Println("Key:", key, "Value:", value) }

Sort the order

import "sort"

var m map[int]string
var keys []int
for k := range m {
    keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
    fmt.Println("Key:", k, "Value:", m[k])
}

Interfaces

# Defining an interface
type Shape2D interface {
    Area() float64
    Perimeter() float64
}
# Type Assertion (Switch)
func DrawShape (s Shape2D) bool {
 switch := sh s.(type) {
     case Rectangle:
     DrawRect(sh)
     case Triangle:
     DrawTriangle(sh)
 }
}

Package Structure

Simple

github.com/soundcloud/simple/ README.md Makefile main.go main_test.go support.go support_test.go

// Package that needs multiple binaries github.com/soundcloud/complex/ README.md Makefile complex-server/ main.go main_test.go handlers.go handlers_test.go complex-worker/ main.go main_test.go process.go process_test.go shared/ foo.go foo_test.go bar.go bar_test.go

Ideal structure depends on the type of your artifacts. If your repo is exclusively a binary or a library, then make sure consumers can go get or import it by its base path. That is, put the package main or primary importable resource in github.com/name/repo, and use subdirectories for helper packages.

If your repo is a combination of binaries and libraries, then you should determine which is the primary artifact, and put that in the root of the repo. For example, if your repo is primarily a binary, but can also be used as a library, then you’ll want to structure it like this:

github.com/peterbourgon/foo/ main.go // package main main_test.go // package main lib/ foo.go // package foo foo_test.go // package foo

One useful thing is to name the package in the lib/ subdirectory for the library rather than the directory, in this case making it package foo rather than package lib.

If your repo is primarily a library, but it also includes one or more binaries, then you’ll want to structure it like this:

github.com/peterbourgon/foo foo.go // package foo foo_test.go // package foo cmd/ foo/ main.go // package main main_test.go // package main

You’ll invert the structure, putting the library code at the root, and making a cmd/foo/ subdirectory to hold the binary code. The cmd/ intermediary is nice for two reasons: the Go tooling automatically names binaries after the directory of their package main, so it allows us to give them the best possible names without potentially conflicting with other packages in the repo; and it makes it unambiguous to your consumers what they’re getting when they go get a path with /cmd/ in the middle.

Working with Time

import (
	"time"
	"fmt"
)

fmt.Println("The time is", time.Now())

Math

import "math"

fmt.Println(math.Pi)

Recursion

func factorial(x uint) uint {
	if x == 0 {
		return 1
	}

	return x * factorial(x-1)
}

Use defer statement to execute func or method when enclosing funtion returns

func data(fileName string) string {
	f := os.Open(fileName)
	defer f.Close()
	contents := io.ReadAll(f)
	return contents
}

Check if file exists

finfo, err := os.Stat("filename.txt")
if err != nil {
    # no such file or dir
    return
}

if finfo.IsDir() {
    # it's a file
} else {
    # it's a directory
}

Create a temp file

file, err := ioutil.TempFile(os.TempDir(), "prefix")
defer os.Remove(file.Name())

Replace a file extension

import "path"

ext := path.Ext(infile)
outfile = infile[0:len(infile)-len(ext)] + ".newExt"

fmt tricks

import "fmt"
a := "the"

# Use [1] to use the same value multiple times
fmt.Printf("%s cat in the %[1]s", a)

JSON

type Person struct {
	name string
	addr string
	phone string
}

p1 := Person{name:"shaun", addr:"Main St.", phone:"123-456-7890"}

// Marshall - Convert to JSON representation
barr, err := json.Marshal(p1)
if err != nil {
    fmt.Println(err)
}

fmt.Println(barr)

# Unmarshalling - Convert to Go object from JSON
var p2 Person
err = json.Unmarshal(barr, &p2)
if err != nil {
    fmt.Println(err)
}

File Access

import (
    "io/ioutil"
)

# Read File
fileData, err := ioutil.ReadFile("test.txt") # not for large files

# Write File
err := ioutil.WriteFile("outfile.txt", fileData, 0777)

# Use os pkg
import (
    "os"
)

f, err := os.Open("test.txt")
barr := make([]byte, 10)
nb, err := f.Read(barr) # Control the amount you read
f.Close()

f, err := os.Create("outfile.txt")
barr := []byte{1,2,3}
nb, err := f.Write(barr)
nb, err := f.WriteString("Hi")
f.Close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment