key features
simplicity
fast compile times
garbagy collected
buillt-in concurrency
compile to standalone binaries
strong and statically typed
Variable declaration
var foo int
var foo int = 42
foo := 42
Cannot redeclare variables, but can shadow them
All variables must be used
Visibility
lower case first letter for package scope
upper case first letter to export
no private scope
Naming conventions
Pascal or camelCase
capitalize acronyms(HTTP,URL)
As short as reasonable
longer names for longer lives
Type conversions
destinationType(variable)
use strconv package for strings
Primitives
Boolean type
zero boolean as false
Numeric types
Intergers
Signed intergers
int type has varying size, but min 32 bits
8 bit(int8) through 64 bits(int64)
Unsigned integers
8 bit(byte and uint8) through 32 bit(uint32)
Arithmetic operations
addition, subtraction, multiplication, division, remainder
Bitwise operations
and, or, xor, and not
Zero value is 0
cannot mix types in the same family
Floating point
IEEE-754 standard
zero value is 0
32 and 64 bit versions
Literal styles
decimal 3,14
exponential 13e18 or 2e10
mixed 13.7e12
Arithmetic operations
addition, subtraction, multiplication, division
Complex numbers
zero values is 0 +0i
64 and 128 nits versions
built-in functions
comlex - make complex number from floart
Text types
Strings
UTF-8
Immutable
Cab be concatenated with + operator
can be converted to []byte
n := 10 // 1010
m := 3 //0011
fmt.Println(n & m) // 0010
fmt.Println(n | m) // 1011
fmt.Println(n ^ m) // 1001
fmt.Println(n &^ m) // 0100
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
// const a int16 = 27
const (
a = iota
b
c
)
const (
a2 = iota
)
const (
//errSpecialList = iota
_ = iota + 5
catSpecialist
dogSpecialist
snakeSpecialist
)
const (
_ = iota
KB = 1 << (10 * iota)
MB
GB
TB
PB
EB
ZB
YB
)
const (
isAdmin = 1 << iota
isHeadquarters
canSeeFinancials
canSeeAfica
canSeeAsia
canSeeEurope
canSeeNorthAmerica
canSeeSouthAmerica
)
func main() {
var role byte = isAdmin | canSeeFinancials | canSeeEurope
fmt.Println(canSeeAfica)
fmt.Printf("%b\n", role)
fmt.Printf("Is admin? %v\n", isAdmin&role == isAdmin)
fmt.Printf("Is HQ? %v\n", isHeadquarters&role == isHeadquarters)
fmt.Printf("HQ? %v\n", isHeadquarters&role)
fileSize := 400000000000.
fmt.Printf("%.2fGB \n", fileSize/GB)
var specilistType int
fmt.Printf("%v\n", specilistType == catSpecialist)
fmt.Printf("%v\n", catSpecialist)
//const a = 14
//var b int16 = 17
fmt.Printf("%v, %T\n", a, a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(a2)
}
fmt.Printf("%v, %T\n", m, m)
fmt.Println("Hello, 世界")
a := 8
fmt.Println(a << 3) // 8 * 2^3 = 2^6 = 64
fmt.Println(a >> 3) // 2^3 / 2 ^3 = 2^0 = 1
k := 13.43
l := 3.2
fmt.Println(k + l)
fmt.Println(k - l)
fmt.Println(k * l)
fmt.Println(k / l)
Constant
Naming convention
Typed constant
untyped constant
enumerated constant
IMMUTABLE, BUT CAN BE SHadowed
replaced by the compiler at compile time
values must be calculated at compile time
named like variables
PascalCase for exported constants
camelCase for internal constants
Typed constants work like immutable variables
can interoperate only with same type
untyped constants work ike literals
can inter
Enumerated constants
Enumerated expressions
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
func main() {
a := []int{}
fmt.Println(a)
a = append(a, 1)
a = append(a, []int{2, 3, 4, 5}...)
fmt.Println(a)
b := a[1:]
fmt.Println(b)
fmt.Println(a)
d := a[:len(a)-1]
fmt.Println(d)
e := append(a[:2], a[3:]...)
fmt.Println(e)
fmt.Println(a)
c := make([]int, 3, 100)
fmt.Println(c)
grades := [3]int{75, 43, 32}
fmt.Printf("grades : %v \n", grades)
var students [3]string
students[0] = "lida"
fmt.Printf("students : %v \n", students)
fmt.Printf("student : %v \n", students[0])
//a := [...]int{1, 2, 3}
//b := &a
//b[1] = 5
//fmt.Println(a)
//fmt.Println(b)
//a := []int{1, 2, 3}
//b := a
//b[1] = 5
//fmt.Println(a)
//fmt.Println(cap(a))
//fmt.Println(len(a))
//fmt.Println(b)
}
Arrays
Creation
Built-in functions
Working with arrays
Collection of items with same type
fixed size
declaration styles
access via zero-based index
len() returns size of array
copies refer to different underlying data
Slices
Creation
Built-in functions
Working with arrays
backed by array
Creation styles
slice exsting array or slice
lireral stlyle
via make function
len()
cap()
append()
copies refer to same underlying array
Maps
what are they?
Creating
Manipulation
Collections of values types that are accessed by keys
Check for presence with "value,ok" form of result
multiple assignments point to the same underlying data
Structs
what are they?
Creating
Naming conventions
Embedding
Tags
Collectiosn of disparate data types that dscribe a single convept
Keys are named fields
structs are value types
anonymous structs are allowed
No inheritance, but can use compisition via embedding
tags can be added to struct fields to describe field
// You can edit this code!
// Click here and start typing.
package main
import (
"fmt"
"reflect"
)
type Doctor struct {
number int
actorName string
episodes []string
companions []string
}
type Animal struct {
Name string `required max:"100"`
Origin string
}
type Bird struct {
SpeedKPH float32
CanFly bool
Animal
}
func main() {
t := reflect.TypeOf(Animal{})
field, _ := t.FieldByName("Name")
fmt.Println(field.Tag)
b := Bird{}
b.Name = "Emu"
b.Origin = "Australia"
b.SpeedKPH = 48
b.CanFly = false
b = Bird{
Animal: Animal{Name: "Emuasd", Origin: "Australia"},
}
fmt.Println(b.Name)
statePopulations := map[string]int{
"california": 109349057,
"texas": 9273459,
"florida": 9348590743,
}
pop, ok := statePopulations["california"]
fmt.Println(pop, ok)
m := map[[3]int]string{}
fmt.Println(statePopulations["california"], m)
a := make(map[string]int)
a["georgia"] = 1923748
delete(a, "georgia")
fmt.Println(a["georgiappp"])
sp := statePopulations
delete(sp, "texas")
fmt.Println(statePopulations, sp)
aDoctor := Doctor{
number: 3,
actorName: "john",
companions: []string{"fs", "sfd"},
}
fmt.Println(aDoctor)
bDoctor := struct{ name string }{name: "John Pertwee"}
fmt.Println(bDoctor)
anotherDoctor := &aDoctor
anotherDoctor.actorName = "Tom"
fmt.Println(anotherDoctor)
fmt.Println(aDoctor)
fmt.Println("Hello, 世界")
}
If statements
Initializer
Comparison operators
Logical operators
Short circuiting
If-else statement
If-else-if statement
Equality and floats
switch statement
switching on a tag
cases with multiple tests
initializers
switches with no tag
fallthrough
package main
import (
"fmt"
"log"
)
func panicker() {
fmt.Println("about to panic")
defer func() {
if err := recover(); err != nil {
log.Println("Error:", err)
panic(err)
}
}()
panic("something happed")
fmt.Println("Done panic")
}
func main() {
fmt.Println("start")
panicker()
fmt.Println("end")
// http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// w.Write([]byte("hello world"))
// })
// err := http.ListenAndServe(":8088", nil)
// if err != nil {
// panic(err.Error())
// }
// a := "start"
// defer func() {
// if err := recover(); err != nil {
// log.Println("Error:", err)
// }
// }()
// //defer fmt.Println("this war derferd")
// panic("something happed")
// a = "end"
// fmt.Println(a)
// a, b := 1, 0
// ans := a / b
// fmt.Println(ans)
//res, err := http.Get("https://www.google.com/robots.txt")
// res, err := http.Get("https://cybernetist.com/2020/05/18/getting-started-with-go-ldap/")
// if err != nil {
// log.Fatal(err)
// }
// defer res.Body.Close()
// robots, err := ioutil.ReadAll(res.Body)
// if err != nil {
// log.Fatal(err)
// }
// fmt.Printf("%s", robots)
// a := "start"
// defer fmt.Println(a)
// a = "end"
}
Defer
used to delay execution of a statement until function exists
useful to group "open" and "close" function together
be careful in loops
Run in LIFO (last-in, first-out) order
arguments evaluated at time defer is executed , not at the time of function called
Panic
Occur when program can not continue at all
donot use when file can not be opened, unless it is critical
use for unrecoverable events - can not obtain TCP port for web server
Function will stop executing
deferred functions will still fire
if nothign handles panic, program will exit
Recover
used to recover from panics
only useful in deferred functions
Current function will not attempt to continue, but higher functions in call stack will
package main
import (
"fmt"
)
type myStruct struct {
foo int
}
func main() {
var ms *myStruct
//ms = &myStruct{foo: 1}
fmt.Println(ms)
ms = new(myStruct)
ms.foo = 42
fmt.Println(ms.foo)
// a := [3]int{1, 2, 3}
// b := &a[0]
// c := &a[1]
// fmt.Println(a, b, c)
//a := 42
//b := &a
// var a int = 42
// var b *int = &a
// //reference. dereference
// a = 27
// *b = 14
// fmt.Println(a, *b)
}
Creating pointers
Pointer types use an asterisk(*) as a prefer to type pointed to
*int - a pointer to an integer
Use the addressof operator (&) to get address of variable
Deferencing pointers
Deference a pointer by putting an asterisk to the pointer
Complex types automatically deference the pointer
Create pointers to objects
can use the addressof operator if value type already exists
use addressof operator before initializer
&myStruct{foo:43}
use the new keyword
can not initialize fields at the same time
Types with internal pointers
all assignment opetations in Go are copy opearatiosn
alices and maps are using pointers to copy
package main
import (
"fmt"
)
func sayGreeting(greeting, name string) {
fmt.Println(greeting, name)
name = "ted"
fmt.Println(name)
}
func sayGreeting1(greeting, name *string) {
fmt.Println(*greeting, *name)
*name = "ted"
fmt.Println(*name)
}
func sum(values ...int) int {
fmt.Println(values)
result := 0
for _, v := range values {
result += v
}
return result
}
func sum1(msg string, values ...int) {
fmt.Println(values)
result := 0
for _, v := range values {
result += v
}
fmt.Println(msg, result)
}
func sum2(msg string, values ...int) *int {
fmt.Println(values)
result := 0
for _, v := range values {
result += v
}
fmt.Println(msg, result)
return &result
}
func divide(a, b float64) (float64, error) {
if b == 0.0 {
return 0.0, fmt.Errorf("can not divide by zero")
}
return a / b, nil
}
type greeter struct {
greeting string
name string
}
func (g greeter) greet() {
fmt.Println(g.greeting, g.name)
g.name = "dfsa"
}
func (g *greeter) greet1() {
fmt.Println(g.greeting, g.name)
g.name = "dfsa"
}
func main() {
g := greeter{
greeting: "heelo",
name: "john",
}
g.greet()
fmt.Println(g.name)
g.greet1()
fmt.Println(g.name)
// for i := 0; i < 5; i++ {
// func() {
// fmt.Println(i)
// }()
// }
// for i := 0; i < 5; i++ {
// func(i int) {
// fmt.Println(i)
// }(i)
// }
// func() {
// fmt.Println("hello")
// }()
// d, err := divide(3.12, 0)
// if err != nil {
// fmt.Println(err)
// return
// }
// fmt.Println(d)
// sumRes2 := sum2("the reuslt is", 1, 2, 3)
// fmt.Println("the sum is ", *sumRes2)
// sumRes := sum(1, 2, 3, 4)
// fmt.Println(sumRes)
// sum1("the result is ", 1, 2, 3)
// greeeting := "heelo"
// name := "stacy"
// sayGreeting(greeeting, name)
// fmt.Println(name)
// sayGreeting1(&greeeting, &name)
// fmt.Println(name)
}
Function
basic syntax
Parameters
Comma delimited list of variables and types
parameters of same type list type once
when pointers are passed in, the function can change the value in the caller
this is always true for slices and maps
use variadic parameters to send list of same types in
must be last parameter
receive as a slice
return values
single return values just list type
multiple return values list types surrounded by parentheses
can return addresses of local variables
automatically promote from the local stack
function as a variable just like int, or slice
Method
function that executes in context of a type
Receiver can be value or pointer
Interface
BAsics
Composing interfaces
Type Conversion
Implementing with values vs pointers
Best practices
package main
import (
"fmt"
"runtime"
"sync"
)
var wg = sync.WaitGroup{}
var counter = 0
var m = sync.RWMutex{}
func sayHello() {
fmt.Println("Hello ", counter)
m.RUnlock()
wg.Done()
}
func increment() {
counter++
m.Unlock()
wg.Done()
}
func main() {
//runtime.GOMAXPROCS(100)
threadsNUm := runtime.GOMAXPROCS(-1)
fmt.Println(threadsNUm)
// for i := 0; i < 10; i++ {
// wg.Add(2)
// m.RLock()
// go sayHello()
// m.Lock()
// go increment()
// }
// wg.Wait()
// var msg = "hello"
// wg.Add(1)
// go func(msg string) {
// fmt.Println(msg)
// wg.Done()
// }(msg)
// msg = "dfad"
// wg.Wait()
// //time.Sleep(100 * time.Millisecond)
// fmt.Println("main go routine finished")
}
Donot create goroutines in libraries
let consumer control concurrency
when creating a goroutine, know how it will end
avoids subtle memory leaks
check for race conditions at compile time
Creating goroutine
Use go keywod
when using anomyous, pass the parameter as local variable
Synchronization
waitGroup to wait for groups of goroutines to complete
Mutex RWMutext to sync
Parallelism
by default, Go will use CPU threads equal to available cores
Change with runtime.GOMAXPROCS
More threads can increase performance, but too many can slow it down
Channel basics
restricting data flow
buffered channels
closing channels
for ... range loops with channels
select statement
func main() {
// s := []int{3, 4, -2, 5, 6}
// c := make(chan int)
// go sum(s[:len(s)/2], c)
// go sum(s[len(s)/2:], c)
// x, y := <-c, <-c
// fmt.Println(x, y, x+y)
// ch := make(chan int, 2)
// ch <- 1
// ch <- 2
// fmt.Println(<-ch, <-ch)
c := make(chan int, 10)
go fibonaci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
func fibonaci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
//close(c)
}
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum
}