The following notes were extracted, adjusted or extended from the following references.
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
$ grep -v "#" /etc/rsyslog.conf
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
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("!!")
}
}
if err != nil {
fmt.Println(err) or log.Println(err) or panic(err)
os.Exit(10)
}
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
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
1. Go compiler
$ go tool compile sourceFile.go
$ ls -ltr sourceFile.o # This is not executable.
$ go tool compile -pack sourceFile.go
$ ls -ltr sourceFile.a
$ ar t sourceFile.a
__.PKGDEF
_go_.o
$ go tool compile -race sourceFile.a
$ 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
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
$ 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
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])
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)
}
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())
}
package main
import (
"fmt"
"time"
)
func main(){
start:=time.Now()
time.Sleep(time.Second)
fmt.Println(time.Since(start))
}
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.
$ echo $GOPATH
/home/percy/go
$ mkdir -p /home/percy/go/src/mypackagefolder
$ touch /home/percy/go/src/mypackagefolder/packageCode.go
package packageCodeImplementation // This is called by external functions
import (
"fmt"
)
func A() {
fmt.Println("This is function A!")
}
const MY=123
$ go install mypackageFolder
Note that “go install mypackageFolder” will create a “mypackageFolder.a” file at “/home/percy/go/pkg/linux_amd64/”
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
$ 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
$ 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)
}