Golang Notes
These are the notes I made while doing the Coursera Golang Specialization
for quick reference.
Objects in Go
- Go does not use the term class.
- A struct is a collection of data.
- Go uses structs with associated methods.
- Go uses a simplified object orientation: - no inheritance - no constructors - no generics
Concurrency
..is the management of multiple tasks that (could theoretically) run at the same time. Concurrent programming enables parallelism:
- management of task execution
- communication between tasks
- synchronization between tasks Gorutines represent concurrent tasks Channels are used to communicate between tasks Select enables task synchronization
Pointer
- .. is an address to data in memory
- & operator returns the address of some data
- operator returns the data at some address
var x int = 1 //declaration and initialisation of an intvar y int //declaration of an intvar ip *int //declaration of a pointer to an intip = &x //setting ip to the address of x > ip now points to xy = *ip //setting y to the data stored at the address ip > y now is x > y = 1----------------------------------------------------var ptr *int = new(int) //new() creates a var and returns pointer to that var, therfore *int*ptr = 3fmt.Println(*ptr) //3fmt.Println(ptr) //some address
Variable Scope
A variable is accessible from block Bj if:
- the variable is declared in Bi and
- Bi >= Bj
Memory: Stack vs Heap
Stack: variables defined within a function - Memory will be deallocated when function call completed (in C for example) Heap: Global variables - Needs to be explicitly deallocated ( in C for example), is fast but error-prone
Type conversion
Use T() to convert, where T stands for the type you want to convert into
var x int32 = 1var x int16 = 2x = y //errorx = int32(y)
ASCII
Each char is associated with an 8-bit number. 8 bits —> 0-255, not enough for different alphabets and symbols
Unicode
Each char is associated with an 32-bit number. Unicode is variable in length. From 8-bits to 32-bits. 8-bit UTF codes are the same as ASCII
Strings
— a sequence of bytes, each byte is a “rune” (Go specific, = UTF-8 code point) Good packages: Unicode (for individual runes), String (for strings)
var hello = "hello"var test = hello[0]fmt.Println(test) //104fmt.Println(string(test)) //h
Switch/case statement
If x is 1, enter case 1, if x is 2, enter case 2…….. Compared to C, no break statements are needed. Go will not fall through, it automatically breaks.
switch x {case 1:fmt.printf("case 1")case 1:fmt.printf("case 2")default:fmt.printf("default case")
Exported names
When importing a package, you can refer only to its exported names. Any "unexported" names are not accessible from outside the package. A name is exported if it begins with a capital letter.
Variables
var i, j int = 1, 2var i, j = 1, 2 //int not needed, var will take the type of the initialiservar c, python, java = true, false, "no!"
Short variable declarations (only within functions)
func main() {var i, j int = 1, 2k := 3 //within fucntions we can omit the var keyword and use :=c, python, java := true, false, "no!"fmt.Println(i, j, k, c, python, java)}
Functions
- Functions are first class - allows functions to be assigned to variables, passed as arguments to other functions and returned from other functions.
func add(x, y int) int {return x + y} //adds x and y, returns integer
Function with multiple results
func swap(x, y string) (string, string) {return y, x} // parameter x and y get swapped, two strings get returned
Named return values
func split(sum int) (x, y int) {x = sum * 4 / 9y = sum - xreturn} //a return statement without arguments, returns the named return values, here (x, y int)
Variables as Functions
Declare a variable as func
- var funcVar func(int) intfunc incFn(x int) int {return x + 1}func main() {funcVar = incFn // function on RHS without ()fmt.Print(funcVar(1))}
Functions as Arguments
- Functions can be passes to another functions as arguments
func applyIt(afunct func (int) int,val int) int {return afunct(val)}func incFn(x int) int {return x + 1}func main() {fmt.Print(applyIt(incFn, 2))}
Anonymous Functions
Don't need to name a function
- func applyIt(afunct func (int) int,val int) int {return afunct(val)}func main() {v := applyIt(func (x int) int{return x + 1}, 2)fmt.Print(v)}
Returning Functions and Closures
Go supports anonymous functions, which can form closures
package mainimport "fmt"func intSeq() func() int {i := 0return func() int { // returns anonymous funci++ // it closes over variable ireturn i // to form closure}}func main() {nextInt := intSeq() // function value captures its own i value,//which will be updated each time we call nextIntfmt.Println(nextInt())fmt.Println(nextInt())fmt.Println(nextInt())newInts := intSeq()fmt.Println(newInts())}
Variadic and Deferred Functions
Variadic means function can take any number of arguments
Variable Argument Number
Functions can take a variable no. of arguments
Uses ellipses ... to specify
Treated as a slice inside function
- func getMax(vals, ...int) int {maxV := -1for _, v := range vals {if v > maxV {maxV = V}}return maxV}
Variadic Slice Argument
can pass slice to a variadic function
Need the ... suffix
- func main() {fmt.Println(getMax(1,3,6,4))vslice := []int{1,3,6,4}fmt.Println(getMax(vslice...))}
Deferred Function Calls
Call can be deferred until the surrounding function completes
Typically used for clean up activities
- func main() {i := 1defer fmt.Println(i+1)i++fmt.Println("Hello")}
Arrays
- fixed length in Go
- Elements are accessed via [ ]
- Index starts at 0
- Elements initialized to zero value by default
var x [5]int //declares array with 5 zeros of int typex[0] = 2________________________//Array literals - An array pre-defined with valuesvar x [5] int = [5]{1,2,3,4,5}x := [...]{1,2,3,4} //... is subsituted with the length of the literal___________________________//Iterating through arryfor i, v range x {fmt.Printf("index %d, value %d", i, v)//range returns two values, the current index and the value at the index
Slices
- A window on an underlying array, contain a pointer to array
- The pointer indicates the start of the array
- The length is the number of elements in the array
- length of each slice is difference between it's declared indices
- The capacity is the max number of elements (from start of slice to end of array)
- capacities are difference between length of underlying array and starting index of slice.
array := [...]int{1,2,3,4,5}slice1 := array[1:3] //2,3slice2 := array[2:5] //3,4,5fmt.Printf(cap(slice2)) // 3, because starting from index 2, the slice can have 3 elements at most
Length and Capacity
- len() function returns length
- cap() function returns capacity
a1 := [3]string{"a","b","c"}sli1 := a1[0:1]fmt.Printf(len(sli1),cap(sli1)) // 1 3
- changing a slice changes the underlying array
- overlapping arrays refer to the same elements, changing it in one slice, also changes it for the other element
Slice literal
- creates the underlying array and then the slice that refers to it
- that means the slice and the array are the same.
- slice points to the start of the array, length equal to capacity
//Sliche literalsli := []{1,2,3} //slices don't need ... in the [ ]
Variable slices
you can also create a slice by using make()
make either takes two to three arguments:
Two: type and length
sli = make([]int, 10)
// then length and capacity are equalThree arguments : : type, length and capacity
sli = make([]int, 10,15)
// underlying array len 15 and slice len 10Append() lets you append elements. If there is space left in the underlying array, it will use that until it is full. Once full and you keep appending, it copies the content and creates a new, bigger underlying array
- sli = make([]int, 0, 3) // len of sli is 0sli = append(sli, 100)fmt.Println(len(s), cap(s)) // 1 3
Hash table
- contains key value pairs
- each value is associated with a unique key
- hash function is used to compute the slot for the key in the hash table. Input=key, output=slot to put value
Maps (Go’s implementation of hash tables)
var idMap map[string]int // key type string value type intidMap = make(map[string]int)_____________________________//Map literalidMap := map[string]int {"joe": 23}_____________________________//Accessing mapsfmt.Printf(idMap["joe"]) //prints 23_____________________________//Adding a key value pairidMap["Jane"] = 34 //no key "Jane" so the key/val pair gets added______________________________//Editing a key value pairidMap["Jane"] = 98 //overrides the old key/val pair____________________________//Deleting a key/val pairdelete(idMap, "joe"_____________________________//Two value assignment to test whether key is in mapid, b := idMap["joe"] //id=23, b=true, because joe is a key in the map_____________________________//Number of key/val pairslen(idMap)_____________________________//Iterating through mapfor key, val := range idMap {fmt.Prinf(key, val)}
Struct
- Aggregate data type
- Groups together objects of arbitrary types
type struct Person {name stringaddr stringphone string}var p1 Personp2 := new(Person) //creates new Person struct with all fields initialized to defaults 0 ir ""p1 := Person(name: "joe", addr: "a str", phone: "0123") //struct literalp1.name = "joe"
Blank identifier
If an assignment requires multiple values on the left side, but one of the values will not be used by the program, a blank identifier on the left-hand-side of the assignment avoids the need to create a dummy variable and makes it clear that the value is to be discarded.
if _, err := os.Stat(path); os.IsNotExist(err) {fmt.Printf("%s does not exist\n", path)}
JSON
- Format to represent structured Data
- Consists of Attribute-Value pairs
- All Unicode
- Types can be combined recursively
//Go structtype struct Person {name stringaddr stringphone string}p1 := Person(name: "joh", age:24, phone:"039203")//JSON{"name": "joh", "age":23, "phone":"0320323"}//Marshal creates JSON byte array from Go structbarr, err := json.Marshal(p1)//Unmarshal converts JSON []byte array into a Go structvar p2 Personerr := json.Unmarshall(barr, &p2)//the Go struct needs to have the same field names as the JSON object has attributes//if Unmarshal worked, err is nil
File Access
- Basic operations: - Open - get handle for access - Read - read bytes into []byte - Write - write []bytes into file - Close - release handle - Seek - move read/write head
- Package “io/ioutil”
dat, err := ioutil.ReadFile("test.txt")// dat is []byte filled with contents of entire file//no open/close needed when using ReadFile//don't use ReadFile with large filesdat = "Hello world"err := ioutul.WriteFile("outfile.txt", dat, 0777)// writes []byte to file, creates new file, unix style permission
OS Package
- os.Open() -returns file descriptor
- os.Close() - close a file
- os.Read() - reads from file into []byte, you can control how big []byte is
- os.Write() - writes []byte into file
f, err := os.Open("test.txt)barr := make([]byte, 10)number, err := f.read(barr)f.close()//fills barr with first 10 bytes from file, calling it the next time it will read the next 10 bytes --> head moves//number = number of bytes read
Methods
There are two types of methods :
- Value receivers : Methods that just do calculations on value, receive value is all they can do
- Pointer receivers : To change a value in struct we need a pointer receiver. You may want to use a pointer receiver type to avoid copying on method calls or to allow the method to mutate the receiving struct
- No need to de-reference pointer with a *
- package mainimport "fmt"type rect struct {width, height int}func (r rect) GetWidth() int {return r.width}func (r *rect) SetWidth(newWidth int) {r.width = newWidth // modifies struct}func main() {r := rect{width: 10, height: 5}fmt.Println("Width: ", r.GetWidth())r.SetWidth(110)fmt.Println("Width: ", r.GetWidth())}
Encapsulation
A package is the smallest unit of private encapsulation in Go.
All identifiers defined within a package are visible throughout that package.
When importing a package you can access only its exported identifiers.
An identifier is exported if it begins with a capital letter.
Example
In this package, the only exported identifiers are
StopWatch
andStart
.package timerimport "time"// A StopWatch is a simple clock utility.// Its zero value is an idle clock with 0 total time.type StopWatch struct {start time.Timetotal time.Durationrunning bool}// Start turns the clock on.func (s *StopWatch) Start() {if !s.running {s.start = time.Now()s.running = true}}The
StopWatch
and its exported methods can be imported and used in a different package.package mainimport "timer"func main() {clock := new(timer.StopWatch)clock.Start()if clock.running { // ILLEGAL// …}}
Interfaces
Go doesn’t have inheritance – instead composition, embedding and interfaces support code reuse and polymorphism
Interfaces is : Set of method signatures
- name parameters, return value
- Implementation is NOT defined
Used to express conceptual similarity between types
interfaces take care of polymorphism and dynamic dispatch.
reference : https://gobyexample.com/interfaces
Type satisfies an interface if type defines all methods specified in the interface.
- same method signatures
Rectangle and Triangle types satisfy the Shape2D interface
- Must have Area() and Perimeter() methods
- Additional methods are OK
- Similar to inheritance and overriding
type Shape2D interface {Area() float64Perimeter() float64}type Triangle {...}func (t Triangle) Area() float64 {...}func (t Triangle) Perimeter() float64 {...}Triangle type satisfies the Shape2D interface, no need to state it explicitly.