Go

References

The following notes were extracted, adjusted or extended from the following references.

General

1. Go inserts only a semicolon at the end of a “{"

 func main()
 {              // <-- this will trigger error
    ....
 }
 func main(){              // <-- this will  NOT trigger error
    ....
 }

2. Install and clean packages

$ go get -v github.com/mastsoud/go/package_name
.....
$ go clean -i -v -x package_name
$ rm -rf ~/go/src/github.com/mastsoud/go/package_name

3. Stdin/out/err

Go Unix
os.Stdin stdin –> /dev/stdin –> /proc/self/fd/0
os.Stdout stdout –> /dev/stdout –> /proc/self/fd/1
os.Stderr stderr –> /dev/stderr –> /proc/self/fd/2

4. Reading input

package main
import (
    "bufio"
    "fmt"
    "os"
)

func main(){
    var f *os.File
    f = os.Stdin
    defer f.Close()
    scanner := bufio.NewScanner(f)
    for scanner.Scan() {
        fmt.Println(">", scanner.Text())
    }
}

5. Logs

  • rsyslogd configuration
$ grep -v "#" /etc/rsyslog.conf
  • Syslog
package main
import (
    "fmt"
    "log"
    "log/syslog"
    "os"
    "path/filepath"
)
func main() {
    programName := filepath.Base(os.Args[0])
    sysLog, err := syslog.New(syslog.LOG_INFO|syslog.LOG_LOCAL7,programName)
    if err != nil {
        log.Fatal(err)
    } else {
        log.SetOutput(sysLog)
    }
    log.Println("LOG_INFO + LOG_LOCAL7: Logging in Go!")
    sysLog, err = syslog.New(syslog.LOG_MAIL, "Some program!")
    if err != nil {
        log.Fatal(err)
    } else {
        log.SetOutput(sysLog)
    }
    log.Println("LOG_MAIL: Logging in Go!")
    fmt.Println("Will you see this?")
}

6. Errors

  • Error types
package main
import (
    "errors"
    "fmt"
)
func returnError(a, b int) error {
    if a == b {
        err := errors.New("Error in returnError() function!")
        return err
    } else {
        return nil
    }
}

func main() {
    err := returnError(1, 2)
    if err == nil {
    fmt.Println("returnError() ended normally!")
        fmt.Println(err)
    }
    err = returnError(10, 10)
    if err == nil {
        fmt.Println("returnError() ended normally!")
    } else {
        fmt.Println(err)
    }
    if err.Error() == "Error in returnError() function!" {
        fmt.Println("!!")
    }
}

  • Typical handling of errors
   if err != nil {
        fmt.Println(err)  or  log.Println(err)    or   panic(err)
        os.Exit(10)
    } 
  • Example
package main
import (
    "errors"
    "fmt"
    "os"
    "strconv"
)
func main() {
    if len(os.Args) == 1 {
        fmt.Println("Please give one or more floats.")
        os.Exit(1)
    }
    arguments := os.Args
    var err error = errors.New("An error")
    k := 1
    var n float64
    for err != nil {
        if k >= len(arguments) {
            fmt.Println("None of the arguments is a float!")
            return
        }
        n, err = strconv.ParseFloat(arguments[k], 64)
        k++
    }
    min, max := n, n
    for i := 2; i < len(arguments); i++ {
        n, err := strconv.ParseFloat(arguments[i], 64)
        if err == nil {
            if n < min {
                min = n
            }
            if n > max {
                max = n
            }
        }
    }
    fmt.Println("Min:", min)
    fmt.Println("Max:", max)
}

7. Using docker

  • Dockerfile
FROM golang:alpine
RUN mkdir /files
COPY hw.go /files
WORKDIR /file
RUN go build -o /files/hw hw.go
ENTRYPOINT ["/files/hw"]
$ docker build -t go_hw:v1 .
$ docker run go_hw:v1

Go internals

1. Go compiler

  • Compiling source file and generate Object code.
$ go tool compile sourceFile.go
$ ls -ltr sourceFile.o  # This is not executable.
  • Genereate an object file instaed of object code
$ go tool compile -pack sourceFile.go
$ ls -ltr sourceFile.a  
  • The following will list the content of *.a file.
$ ar t sourceFile.a
__.PKGDEF
_go_.o

  • To detect trace conditions
$ go tool compile -race sourceFile.a
  • Showing assembly code
$ go tool compile -S sourceFile.go

os.(*File).close STEXT dupok nosplit size=26 args=0x18 locals=0x0
	0x0000 00000 (<autogenerated>:1)	TEXT	os.(*File).close(SB), DUPOK|NOSPLIT|ABIInternal, $0-24
	0x0000 00000 (<autogenerated>:1)	PCDATA	$0, $-2
	0x0000 00000 (<autogenerated>:1)	PCDATA	$1, $-2
	0x0000 00000 (<autogenerated>:1)	FUNCDATA	$0, gclocals·e6397a44f8e1b6e77d0f200b4fba5269(SB)
	0x0000 00000 (<autogenerated>:1)	FUNCDATA	$1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
	0x0000 00000 (<autogenerated>:1)	FUNCDATA	$2, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
	0x0000 00000 (<autogenerated>:1)	PCDATA	$0, $1
	0x0000 00000 (<autogenerated>:1)	PCDATA	$1, $1
	0x0000 00000 (<autogenerated>:1)	MOVQ	""..this+8(SP), AX
	0x0005 00005 (<autogenerated>:1)	MOVQ	(AX), AX
	0x0008 00008 (<autogenerated>:1)	PCDATA	$0, $0
	0x0008 00008 (<autogenerated>:1)	PCDATA	$1, $0
	0x0008 00008 (<autogenerated>:1)	MOVQ	AX, ""..this+8(SP)
	0x000d 00013 (<autogenerated>:1)	XORPS	X0, X0

...

2. Garbage collector

  • Example of GC
package main
import (
    "fmt"
    "runtime"
    "time"
)
func printStats(mem runtime.MemStats) {
    runtime.ReadMemStats(&mem)
    fmt.Println("mem.Alloc:", mem.Alloc)
    fmt.Println("mem.TotalAlloc:", mem.TotalAlloc)
    fmt.Println("mem.HeapAlloc:", mem.HeapAlloc)
    fmt.Println("mem.NumGC:", mem.NumGC)
    fmt.Println("-----")
}

func main() {
    var mem runtime.MemStats
    printStats(mem)
    for i := 0; i < 10; i++ {
        s := make([]byte, 50000000) // memory allocation
        if s == nil {
            fmt.Println("Operation failed!")
        }
    }
    printStats(mem)
    for i := 0; i < 10; i++ {
        s := make([]byte, 100000000) // MORE memory allocation
        if s == nil {
            fmt.Println("Operation failed!")
        }
        time.Sleep(5 * time.Second)
    }
    printStats(mem)
}

$ go run gColl.go
  • To get MORE information
$ GODEBUG=gctrace=1 go run gColl.go

gc 1 @0.027s 0%: 0.048+0.72+0.007 ms clock, 0.38+0.18/0.62/1.1+0.057 ms cpu, 4->4->0 MB, 5 MB goal, 8 P
gc 2 @0.047s 0%: 0.006+0.56+0.007 ms clock, 0.055+0.39/0.69/0.79+0.061 ms cpu, 4->4->0 MB, 5 MB goal, 8 P
gc 3 @0.060s 1%: 0.16+1.0+0.011 ms clock, 1.2+1.3/1.1/0.13+0.093 ms cpu, 4->4->0 MB, 5 MB goal, 8 P
gc 4 @0.069s 1%: 0.016+0.54+0.016 ms clock, 0.13+0.16/0.61/1.0+0.12 ms cpu, 4->4->0 MB, 5 MB goal, 8 P
gc 5 @0.073s 1%: 0.002+0.37+0.003 ms clock, 0.023+0/0.37/1.0+0.028 ms cpu, 4->4->0 MB, 5 MB goal, 8 P
gc 6 @0.076s 1%: 0.016+0.41+0.011 ms clock, 0.13+0.14/0.60/0.40+0.088 ms cpu, 4->4->1 MB, 5 MB goal, 8 P
gc 7 @0.079s 1%: 0.002+0.34+0.010 ms clock, 0.020+0.14/0.36/0.82+0.081 ms cpu, 4->4->0 MB, 5 MB goal, 8 P
# command-line-arguments
gc 1 @0.001s 10%: 0.002+1.3+0.011 ms clock, 0.018+0.82/1.4/2.0+0.092 ms cpu, 5->6->6 MB, 6 MB goal, 8 P
gc 2 @0.010s 7%: 0.004+3.4+0.011 ms clock, 0.036+0.42/5.3/3.8+0.090 ms cpu, 13->14->13 MB, 14 MB goal, 8 P
gc 3 @0.044s 4%: 0.017+4.8+0.011 ms clock, 0.13+0.18/8.2/18+0.093 ms cpu, 25->25->23 MB, 26 MB goal, 8 P
mem.Alloc: 125896

Taking the example of “4->4->0”:

    - The first number is the heap size when the garbage collector is about to run. 
    - The second value is the heap size when the garbage collector ends its operation. 
    - The last value is the size of the live heap

3. GC internals

GC in slices

GC in maps

GC in maps without pointers

GC in spliting maps

GC comparisons`

GC unsafe code

GC unsafe package

Defer keyword

Defer keyword using logging

Panic

Recover

Strace

dtrace

Go environment

Node trees

Go build

WebAssembly code

Data types

1. Slices

  • Slices are passed by reference to functions.

  • Slices are often used, more than arrays.

 mySlcie := []int{1,23,4}
 mySlice := make([]int, 20)  // Go initilise with default values
 mySlice = append(mySlice, 2134)
 len(mySlice)
 fmt.Println(mySlice[1:3])
  • Re-slicing may cause some problems.

Reslice do not copy values, reslice keeps reference from the orignal slice. Therefore, if you make any change of values in the reslice, they will also change values in the original slice.

package main
import "fmt"
func main() {
    s1 := make([]int, 5)
    reSlice := s1[1:3]  // Reslice not copy from s1, it make reference
    fmt.Println(s1)
    fmt.Println(reSlice)
    reSlice[0] = -100  // This means also s[1]=-100
    reSlice[1] = 123456 // This also means s[2]=123456
    fmt.Println(s1)
    fmt.Println(reSlice)
}

Output

$ go run reslice.go
[0 0 0 0 0]
[0 0]
[0 -100 123456 0 0]
[-100 123456]
  • If the length and the capacity of a slice have the same values and you try to add another element to the slice, the capacity of the slice will be doubled whereas its length will be increased by one.

  • Byte slices

s := make([]byte,5)


**2. Copy slices**

- Becareful using copy(destination, source), as copy() copies the minimum number of len(dst) and len(src) elements

**3. Sort slice**

**4. Appending arrays to slices** 


**5. Maps**

- Declaration

```bash
iMap = make(map[string]int)
delete(mapName, Key)

for key, value := range iMap {
    fmt.Println(key, value)
}

The bad thing is that if you try to get the value of a map key that does not exist in the map, you will end up getting zero, which gives you no way of determining whether the result was zero because the key you requested was not there or because the element with the corresponding key actually had a zero value. This is why we have _, ok in maps.


_, ok := iMap["doesItExist"]
if ok {
    fmt.Println("Exists!")
} else {
    fmt.Println("Does NOT exist")
}

Please note that you cannot and should not make any assumptions about the order the map pairs are going to be displayed on your screen because that order is totally random.

The next Go code will not work because you have assigned the nil value to the map you are trying to use:

aMap := map[string]int{}
// var aMap map[string]int
aMap = nil
fmt.Println(aMap)
aMap["test"] = 1

Saving the preceding code to failMap.go and trying to compile it will generate the next error message:

$ go run failMap.go
map[]
panic: assignment to entry in nil map

This means that trying to insert data to a nil map will fail. However, looking up, deleting, finding the length, and using range loops on nil maps will not crash your code.

6. Constants

7. Pointers

When working with pointers, you need * to get the value of a pointer, which is called dereferencing the pointer, and & to get the memory address of a non-pointer variable

package main
import (
    "fmt"
)
func getPointer(n *int) {
    *n = *n * *n

    fmt.Println(&n)
}
func returnPointer(n int) *int {
    v := n * n
    return &v
}
func main(){
    n:=3
    getPointer(&n)
    fmt.Println(n)

    k := returnPointer(12)
    fmt.Println(*k)
    fmt.Println(k)
}

Pointers allow you to share data, especially between Go functions. Pointers can be extremely useful when you want to differentiate between a zero value and a value that is not set

Time

package main
import (
    "fmt"
    "time"
)
func main(){
    fmt.Println(time.Now())
    time.Sleep(time.Second*2)
    t:=time.Now()
    fmt.Println(t.Day(),t.Month(), t.Year())
    t2:=time.Now()
    fmt.Println(t2.Sub(t))
    fmt.Println("time units", time.Nanosecond , time.Microsecond , time.Millisecond , time.Minute , time.Hour)
}
  • Parsing date +time from a string:
CODE Meaning
2006 Year
Jan Month
02 Day
15 Hour
04 Minute
05 Second
package main
import (
    "fmt"
    "time"
)
func main(){
    test:="20201025 15:20:33"
    d,_:=time.Parse("20060102 15:04:05",test)
    fmt.Println(d.Hour(),d.Minute(),d.Second())
    fmt.Println(d.Year(),d.Month(),d.Day())
}
  • Measure time execution
package main
import (
    "fmt"
    "time"
)
func main(){
    start:=time.Now()
    time.Sleep(time.Second)
    fmt.Println(time.Since(start))
}

Composite Types

1. Struct

package main
import (
    "fmt"
)

type XY struct{
    x int
    y int
}

func returnPointer(x,y int) *XY {
    x++
    y++
    return &XY{x,y}
}
func returnStruct(x,y int) XY {
    x--
    y--
    return XY{x,y}
}
func main(){
    s1:=returnPointer(3,4)
    s2:=returnStruct(3,4)
    fmt.Println((*s1).x)  // You need a pointer reference in this case
    fmt.Println(s2.x)
}

2. Tuples

package main
import (
"fmt"
)
func retThree(x int) (int, int, int) {
return 2 * x, x * x, -x
}
func main() {
    fmt.Println(retThree(10))
    n1, n2, n3 := retThree(20)
    fmt.Println(n1, n2, n3)
    n1, n2, n3 =  n3,n2,n1  //swap
    fmt.Println(n1, n2, n3)
}


**3. Json""



**4. XML""


- 


### Data Structures





### Functions

**1. Return values of function

```bash
func namedMinMax(x, y int) (min, max int) {
    if x > y {
        min = y
        max = x
    } else {
        min = x
        max = y
    }
    return 
}

Note that the return as this function has named return values in its signature, the min and max parameters are automatically returned in the order in which they were put into the function definition.

2. FUcntions with pointer parameters


func getPtr(v *float64) float64 {
    return *v * *v
}

func main() {
    x := 12.2
    fmt.Println(getPtr(&x))
}

3. Functions that return pointers

func returnPtr(x int) *int {
    y := x * x
    return &y
}

func main() {
    sq := returnPtr(10)
    fmt.Println("sq value:", *sq)
    fmt.Println("sq memory address:", sq)
}

4. Functions that return other functions

func funReturnFun() func() int {
    i := 0
    return func() int {
                i++
                return i * i
            }
}

func main() {
    i := funReturnFun()
    j := funReturnFun()
    mt.Println("1:", i())
    fmt.Println("2:", i())
    fmt.Println("j1:", j())
    fmt.Println("j2:", j())
    fmt.Println("3:", i())
}

Executing returnFunction.go will produce the following output:

$ go run returnFunction.go
1: 1
2: 4
j1: 1
j2: 4
3: 9

As you can see from the output of returnFunction.go , the value of i in funReturnFun() keeps increasing and does not become 0 after each call either to i() or j() .

5. Functions that accept other functions as paramters

func function1(i int) int {
    return i + i
}
func function2(i int) int {
    return i * i
}
func funFun(f func(int) int, v int) int {
    return f(v)
}
func main() {
    fmt.Println("function1:", funFun(function1, 123))
    fmt.Println("function2:", funFun(function2, 123))
    fmt.Println("Inline:", funFun( func(i int) int { 
                                        return i * i*i
                                        }, 123))
}

Executing funFun.go will produce the next output:

$ go run funFun.go
function1: 246
function2: 15129
Inline: 1860867

6. Varadic functions

func varFunc(input ...string) {
    fmt.Println(input)
}

func oneByOne(message string, s ...int) int {  # accepts a single string and a variable number of integer arguments.
    fmt.Println(message)
    sum := 0
    for i, a := range s {
        fmt.Println(i, a)
        sum = sum + a
    }
    s[0] = -1000
    return sum
}
func main() {
    arguments := os.Args
    varFunc(arguments...)
    sum := oneByOne("Adding numbers...", 1, 2, 3, 4, 5, -1, 10)
    fmt.Println("Sum:", sum)
    s := []int{1, 2, 3}
    sum = oneByOne("Adding numbers...", s...)
    fmt.Println(s)
}

The input function argument is a slice and will be handled as a slice inside the varFunc() function. The … operator used as …Type is called the pack operator, whereas the unpack operator ends with … and begins with a slice. A variadic function cannot use the pack operator more than once.

Building and executing variadic.go will generate the following output:

$ ./variadic 1 2
[./variadic 1 2]
Adding numbers...
0 1
1 2
2 3
3 4
4 5
5 -1
6 10
Sum: 24
Adding numbers...
0 1
1 2
2 3
[-1000 2 3]

7. Packages

  • How to deploy own package.

    • Verify where go stores package source code.
    $ echo $GOPATH
    /home/percy/go
    
    • Create a directory inside ~/go/src. This folder will be installed by “go install”
    $ mkdir -p /home/percy/go/src/mypackagefolder
    $ touch /home/percy/go/src/mypackagefolder/packageCode.go
    
    • Copy the package source code inside packageCode.go. For example
    package packageCodeImplementation     // This is called by external functions
    
    import (
        "fmt"
    )
    func A() {
        fmt.Println("This is function A!")
    }
    
    const MY=123
    
    • Install mypackageFolder, not the packageCode.go.
    $ go install mypackageFolder
    

    Note that “go install mypackageFolder” will create a “mypackageFolder.a” file at “/home/percy/go/pkg/linux_amd64/”

    • Import mypackageFolder from another go code, for example.
    package main
    
    import (     // Imports the FOLDER where the package is located.
        "mypackageFolder"   // this calls mypackageFolder, not the packageCode and not packageCodeImplementaion.
        "fmt"
    )
    func main() {   
        fmt.Println("Using packageCode!")
        // this calls the pacakgeCodeImplemetation, not the mypackageFolder nor packageCode
        packageCodeImplementation.A() 
        fmt.Println(packageCodeImplemenation.MY)
    }
    

NOTE: The following applies to FUNCTIONS, VARIABLES, TYPES, ETC.

Name comment
fmt.Println Functions that start with UPPER case are PUBLIC
fmt.myPrint It starts with LOWER case, it is PRIVATE

8. Init()

Every Go package can optionally have a private function named init() that is automatically executed at the beginning of the execution time. The init() function is a private function by design, which means that it cannot be called from outside the package in which it is contained. Additionally, as the user of a package has no control over the init() function, you should think carefully before using an init() function in public packages or changing any global state in init() .

Init() is executed only once at the time where the import calls the package. For example:


package a
import (
    "fmt"
)
func init() {
    fmt.Println("init() a")
}
func FromA() {
    fmt.Println("fromA()")
}
package b
import (
    "a"
    "fmt"
)
func init() {
    fmt.Println("init() b")
}
func FromB() {
    fmt.Println("fromB()")
    a.FromA()
}
package main
import (
    "a"
    "b"
    "fmt"
)
func init() {
    fmt.Println("init() manyInit")
}
func main() {
    a.FromA()
    b.FromB()
}

Will results as:

$ go run manyInit.go
init() a
init() b
init() manyInit
fromA()
fromB()
fromA()

9. Modules

Go modules allow you to write things outside of GOPATH. Modules are used to specify dependencies and their locations.

For example


percy@prec:m$ mkdir test
percy@prec:m$ cd test
percy@prec:m$ touch test.go
percy@prec:m$ cat test.go 
package main
import (
    v1 "github.com/percyperezdante/gomod"
)

func main() {
    v1.Version()
}

requires github.com/percyperezdante/gomod which is in github.com and contain the following:

package gomod

import (
	"fmt"
)

func Version() {
	fmt.Println("Version 1.0.0")
}

If you run at this stage you will get a similar error as the following:

test.go:3:5: cannot find package "github.com/percyperezdante/gomod" in any of:
	/usr/local/go/src/github.com/percyperezdante/gomod (from $GOROOT)
	/home/percy/go/src/github.com/percyperezdante/gomod (from $GOPATH)

To execute test.go you could run the following:

$ cd test
$ export GO111MODULE=on
$ go mod init anyname
$ vim go.mod
$ cat go.mod
   module anyname

   go 1.14

   require github.com/percyperezdante/gomod v1.0.0

And then

$ go run test.go

NOTE

  • To remove this module:
$ go env GOPATH
  /usr/local/go
$ cd $GOPATH/pkg/mod/github.com/percyperezdante
$ ls -ltr

dr-x------ 2 percy percy 4096 May 21 08:47 gomod@v1.0.0
dr-x------ 2 percy percy 4096 May 21 08:58 gomod@v1.1.0

$ rm -rf gomod@v1.0.0
$ cd $GOPATH/pkg/mod/cache/download/github.com/percyperezdante
$ rm -rf gomod
  • To keep all dependencies in the current directory
$ cd test
$ go mod init anyname
$ go mod vendor

Go downloads all dependencies in inside the test/vendor folder

10. Type assertions

A type assertion is the x.(T) notation, where x is an interface type and T is a type. Additionally, the actual value stored in x is of type T and T must satisfy the interface type of x.

func main() {
    var myInt interface{} = 123    // This declares and defines an interface
    k, ok := myInt.(int) 
    if ok {
        fmt.Println("Success:", k)
    }
    v, ok := myInt.(float64)
    if ok {
        fmt.Println(v)
    } else {
        fmt.Println("Failed without panicking!")
    }

    i := myInt.(int)    // This is int and = 123
    fmt.Println("No checking:", i)
    j := myInt.(bool)  // This will fail as it myInt is int not boolean
    fmt.Println(j)
}