2020-01-06
These notes are extracted from the following book: The little Go Book
var port int
// declarationport = 3000
// assignment :=
port := 3000
:=
per scope for one variable except if other variable as follow:secondVar, port := "val1", 4000
func <name>( types ) returnType { ... }
func log(message int) { ... }
func add(a int, b int) int { ...}
func power(name string) (int, bool) {...} // two value return
val, exists := power("goku")
// use _ if want to discard one value (return value not assign):
_, exists := power("goku")
type Saiyan struct {
Name string
Power int
}
goku := Saiyan{ Name: "Goku", Power: 9000, }
goku := Saiyan{}
goku := Saiyan{ "Goku", 9000} // same order as the fields declarations
goku := &Saiyan{ Name: "Goku", Power: 9000}
goku := new Saiyan() // same as &Saiyan{} but without initialisation of fields
==> new allocate memory
func (s *Saiyan) Super(){
s.Power += 10000
}
func NewSaiyan(name string, power int) Saiyan {
return Saiyan{ name, power }
}
// no need to return a pointer to Saiyan
type Saiyan struct {
Name string
Power int
Father *Saiyan
}
gohan:= &Saiyan{
Name: "Gohan"
Power: 1000,
Father: &Saiyan{
Name: "Goku",
Power: 10000,
Father: nil
},
}
Act of including one structure into another
In some language it's called a trait
Example in Java:
public class Person {
private String name;
public String getName(){ return this.name; }
}
public class Saiyan{
private Person person;
public String getName(){ return this.Person.getName(); }
}
Example in Go
type Person struct {
Name string
}
func (p *Person) Introduce(){ fmt.PrintF("Hi, I'm %s\n", p.Name)}
type Saiyan struct {
*Person
Power int
}
// Use
goku:= &Saiyan{ Person: &Person{"Goku"}, Power:9000, }
goku.Introduce() // work
Bc we didn't give explicit field name to *Person
, we can implicitly access the field
and functions for the compose type. However Go compiler did give it a field name
Example :
goku := &Saiyan{
Person: &Person{"Goku"},
}
fmt.Println(goku.Name) // Goku
fmt.Println(goku.Person.Name) // Goku
func (s *Saiyan) Introduce(){
fmt.Printf("Hi, I'm %s. Ya!\n", s.Name)
}
Composed version is always available via s.Person.Introduce()
var scores [10]int
scores[0] = 339
scores[0] β------------β scores[9]
scores := [4]int{ 900, 400, 255, 33}
for index, value := range scores {
// logic
}
Lightweight structure that wraps and represent a portion of an array
There are few ways to create a slice
A slice is not an array. A slice describes a piece of an array
Interested link: Slices
Simple
slice isnβt declared with a length within the square brackets
scores []int{ 10, 20, 30, 40} // no size within bracket
make
more than just allocating memory (new)
scores := make([]int, 5)
[ 0, 0, 0, 0, 0 ]
scores := make([]int, 0, 10) // nil slice nb elements unknown
[]
Notes
scores = append(scores,5)
Is slice a copy ?
scores := []int{1,2,3,4,5}
slice := scores[2:4]
slice[0] = 999
fmt.Println(scores)
[1, 2, 999, 4, 5]
Use copy function
copy(newSlice, slice)
The copy function also gets things right when source and destination overlap
It can be used to shift items around in a single slice.
Shorcuts
slice[X:] // from X to the end
slice[:X] // from the start to X
slice[:] // all element of a slice
Maps, like slices, are created with the make function
key and value -> get, set and delete values from it
created with make
make
lookup := make(map[string]int)
lookup["goku"] = 9001
power, exists := lookup["vegeta"]
power -> 0 default value
exists -> 0, false
operations
// len: get number of keys
total := len(lookup)
// delete: remove a value based on its key
delete(lookup, "goku")
// size: Map grow dynamically, however, can supply intial size in make
lookup := make(map[string]int, 100)
//Could help performance if the number of key is known and size defined
map as field structure
type Saiyan struct{
Name string
Friends map[string] *Saiyan
}
goku := &Saiyan{
Name: "Goku",
Friends: make(map[string*Saiyan),
}
goku.Friends["krillin"]= //load or create krillin
==> ALTERNATIVE TO INITIALIZATION
lookup := map[string]int{
"goku" : 9001,
"gohan": 2044,
}
// iteration possible wiht range
for key, value := range lookup {
// logic
}
ITERATION OVER MAP ISN'T ORDERED
--> RETURN THE KEY IN A RANDOM ORDER
VISIBLE
NOT VISIBLE
go get github.com/mattn/go-sqlite3
go get
fetch the remote files and stores them in your workspaces
import (
"github.com/mattn/go-sqlite3"
)
goop
or godep
for more advance dependancy manager without some issue of version...Types taht define a contract but not an implementation
type Logger interface {
Log(message string)
}
type Server struct {
logger Logger
}
func process(logger Logger){
logger.Log("Hello")
}
type error interface {
Error() string
}
import (
"errors"
)
func process (count int) error {
if count < 1 {
return errors.New("invalid count")
}
...
return nil
}
func main(){
file, err := os.Open("a_file_to_read")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
// read the file
}
if x := 10; count > x {
}
real example:
if err := process(); err != nil {
}
value aren't available outside the if-statement, but inside any else if or else blockGo doesn't have any inheritance neither superclass
But it does have empty interface with no methods:
interface {}
Since every type implement all of the empty interface's methods
And since interface are implicitly implemented
func add ( a interface{}, b interface{}) interface{} {
// logic
}
to convert an interface variable to an explicit type, use .(TYPE)
func add ( a interface{}, b interface{}) interface{} {
return a.(int) + b.(int)
}
if the underlying type is not int ---> error
switch a.(type) {
case int:
fmt.Printf("val %d\n",a)
case bool, string:
// ...
default:
// ...
}
string and byte array are closely related
We can easily convert one to the other
example:
stra := "the spice must flow"
byts := []byte(stra)
strb := string(byts)
When using []byte(X)
or string(X)
, you're creating a copy of the data necessary because string are immutable
Strings are made of runes which are unicode code points
Functions are first-class types
type Add func(a int, b int) int
Which can then be used anywhere - as a field type, as a parameter, as a return value
example:
type Add func(a int, b int) int
func main() {
fmt.Println(process(func(a int, b int) int {
return a + b
}))
}
func process(adder Add) int {
return adder(1,2)
}
Similar to thread, but it is scheduled by go, not the OS
Code runs in goroutine can cur concurrently with other
Example:
import (
"fmt"
"time"
)
func main() {
fmt.Println("start")
go process()
time.Sleep(time.Millisecond * 10)
fmt.Println("done")
}
func process() {
fmt.Println("processing")
}
Start a go routine by using the keyword "go" followed by the function to exec
We can use an anonymous function for bit of code as following
// anonymous func are not only use by goroutines
go func() {
fmt.Println("processing")
} ()
IMPORTANT:
The process doesnβt wait until all goroutines are finished before exiting
To solve this, we need to coordinate our code
To help the problem of synchronisation, Go provvide channels
The only concurrent thing you can safely do to a variable is to read from it.
You can have as many readers as you want, but writes need to be synchronized
The most common approach is to use a mutex:
Example:
package main
import ( "fmt"; "time"; "sync")
var (
counter = 0
lock sync.Mutex
)
func main() {
for i:=0; i< 20; i++ {
go incr()
}
time.Sleep(time.Millisecond * 10)
}
func incr() {
lock.Lock()
defer lock.Unlock()
counter++
fmt.Println(counter)
}