Skip to content

Instantly share code, notes, and snippets.

@CodeFreezr
Forked from komuw/01_golang.md
Created January 27, 2018 16:42
Show Gist options
  • Save CodeFreezr/eb229eb46281291f9dedd5234f5fc7e0 to your computer and use it in GitHub Desktop.
Save CodeFreezr/eb229eb46281291f9dedd5234f5fc7e0 to your computer and use it in GitHub Desktop.
my golang notes
/*
1. Intro and download
url = https://storage.googleapis.com/golang/go1.4.2.linux-386.tar.gz #32bit
url = https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz #64bit
$ sudo wget --directory-prefix=/usr/local url
$ sudo tar --directory=/usr/local -xzf go1.4.2.linux-amd64.tar.gz
# add the line
export PATH=$PATH:/usr/local/go/bin
#to /etc/profile(system-wide install) or $HOME/.profile
//some references
# https://golang.org/doc/effective_go.html
# https://gobyexample.com
# https://tour.golang.org
# http://golang.org/doc/code.html #how to organise ua workspace
# https://www.youtube.com/watch?v=XCsL89YtqCs #how to organise ua workspace(video)
# https://lincolnloop.com/blog/introduction-go-debugging-gdb/ #pdb like debugging
# https://cgdb.github.io/ #debug
# https://github.com/golang/go/wiki#additional-go-programming-wikis
*/
/*
# create golang code dir #ie workspace
$ mkdir $HOME/work/gocode
$ export GOPATH=$HOME/work/gocode #add to ~/.profile
$ mkdir -p $HOME/work/gocode/src/github.com/komuw
$ cd $HOME/work/gocode/src/github.com/komuw
$ mkdir hello
$ cd hello
$ nano hello.go
*/
package main
import "fmt"
func main() {
fmt.Printf("hello, world\n")
}
/*
$ go run hello.go
$ go install #install the binary
# it gets installed in $HOME/work/gocode/bin
$ ls $HOME/work/gocode/bin
$ $HOME/work/gocode/bin/hello #to run it
$ export PATH=$HOME/work/gocode/bin:$PATH #add bin to path
#add to ~/.profile
$ hello #now u can run hello from anywhere
#lets create a package(can be imported by other packages)
#they have a package name other than main
$ cd $HOME/work/gocode/src/github.com/komuw
$ mkdir string
$ cd string and touch string.go
*/
package string //package name
func Reverse(s string) string {
b := []byte(s)
for i := 0; i<len(b)/2; i++ {
j := len(b)-i-1
b[i], b[j] = b[j], b[i]
}
return string (b)
//NB: The fxn begins with capital letter, so that it can be exported and thus able to be imported by other packages.
}
/*
$ go build #check that it compiles
$ go install # make it available
$ ls $HOME/work/gocode/pkg/linux_amd64/github.com/komuw/
string.a
#lets use that package
$ cd hello && nano hello.go
*/
package main
import (
"fmt"
"github.com/komuw/string"
)
func main() {
fmt.Printf(string.Reverse("hello, world\n"))
}
/*
$ go install
$ hello #run
#tests
$ cd string
$ touch string_test.go #test files live in same dir and end in _test
$ go test #run tests
#ls ~/.profile
#golang
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/work/gocode
export PATH=$HOME/work/gocode/bin:$PATH
*/
//I. BASICS
package main
import "fmt"
func main() {
fmt.Println("Hello,")
}
//1. Packages
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("My favorite number is", rand.Intn(10))
}
//every prog is made of packages.
//progs start running in package main
// the package name is the same as the last element of the import path.
//"math/rand" package comprises files that begin with the statement package rand.
//Imports: u can do in each line or group em using ()
import "fmt"
import "math"
//Exports: After importing a package, you can refer to the names it exports.
//a name is exported if it begins with a capital letter.
// u cant use math.pi but u can use math.Pi
//2. Functions
package main
import "fmt"
func add(x int, y int) string {// this fxn will return a string
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
//fxns can take zero or more args. note that the arg types come after arg_name
//u can do
func add(x, y int) //if the args share same type
//functions can return multiple values
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
//return values can be named
func split(a int) (x, y int) { //the named return values here are named x & y
x = a * 4 / 9
y = a - x
return //this is naked return, it returns current values(x,y) of the results; its discouraged though.
}
func main() {
fmt.Println(split(17))
}
//3. VARIABLES
var c, python, java bool
func main() {
var i int
fmt.Println(i, c, python, java)
}
//var declares variables
//it can be at package, or function level
//vars can have intializers, in which case u may omit the type(var will infer type from the intializer)
var c, python, java = true, false, "no!"
//inside a fxn u can use the := shorthand to declare vars. := does type inference automatically
func main() {
var i, j int = 1, 2
k := 3
c, java := true, "no!"
fmt.Println(i, j, k, c, python, java)
}
//u cant use := outside a fxn
//4. BASIC TYPES
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32 // represents a Unicode code point
float32 float64
complex64 complex128
//vars can be grouped just like imports
import (
"fmt"
"math/cmplx"
)
var (
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)
// a << b == a * 2^b (left shift)
// a>>b == a / 2^b (right shift)
//thus
1 << 64 - 1 == (1 * 2^64) - 1
//vars that aren't initialized are given their zero values
0 for numeric types. //var f float64
false for boolean. //var b bool
"" for strings. //var s string
//Type conversions:
var i int = 42
var f float64 = float64(i)
//Or, put more simply:
i := 42
f := float64(i)
//Constants
const Pi = 3.14
func main() {
const World = " "
fmt.Println("Hello", World)
fmt.Println("Happy", Pi, "Day")
}
//declared with const keyword
//they can be char, string, numeric or boolean
//u cant declare em using :=
//5. A little bit about some go builttin types: golang.org/pkg/builtin/
//int32 is the set of all signed 32-bit integers ie -(2**32)/2 to +(2**32)/2
//ie range -2billion - +2billion
//int64 is the set of all signed 64-bit integers. ie -(2**64)/2 to +(2**64)/2
//int8 is the set of all signed 8-bit integers. Range: -128 to 127. -(2**8)/2 to +(2**8)/2
//rune is an alias for int32 and is equivalent to int32 in all ways.
//It is used, by convention, to distinguish character values from integer values.
//uint32 is the set of all unsigned 32-bit integers. Range: 0 to 4294967295. 0 to +2**32
//byte is an alias for uint8 and is equivalent to uint8 in all ways.
//It is used, by convention, to distinguish byte values from 8-bit unsigned integer values.
//uint8 is the set of all unsigned 8-bit integers. Range: 0 to 255.
//5. Runes, Bytes, strings
var y rune
y = 'a'
x = '⌘'
fmt.Println(y)
//prints 97
//bcoz the char 'a' is the unicode codepoint U+0061 (or 97 in ints)
fmt.Println(x) //8984
//NB:
var y rune
y = 'abv'
fmt.Println(y)
//will raise error bcoz runes arent strings but 'abv' is a string
var myb byte
myb = 'a'
myb2 = '⌘'
fmt.Println(myb) //97
fmt.Println(myb2) //raises err 'constant 8984 overflows byte'
//bcoz remember; bytes are likke uint8 (0-255) and 8984 is greater than 255
func main() {
s := []byte("ab")
fmt.Println(s) //outputs: [97 98] . char 'b' ==int 98 unicode
fmt.Println(string(s)) //outputs 'ab'
}
//"strings are good for human readable text and bytes for pretty much anything else." - http://vluxe.io/golang-bytes.html
const sample = "\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98"
fmt.Println(sample) //outputs: ��=� ⌘
for i := 0; i < len(sample); i++ {
fmt.Printf("%x ", sample[i]) //%x (hexadecimal) format
//use "%+q" to show quoted string ie(its unicode representation)
}
//outputs: bd b2 3d bc 20 e2 8c 98
//indexing a string accesses individual bytes, not characters.
//when we store a character value in a string, we store its byte-at-a-time representation
//A for range loop, by contrast to a normal for loop, decodes one UTF-8-encoded rune on each iteration.
//Each time around the loop, the index of the loop is the starting position of the current rune, measured in bytes,
//and the code point is its value.
const nihongo = "日本語"
for key, value := range nihongo {
fmt.Printf("%#U starts at byte position %d\n", value, key)
}
//outputs:
U+65E5 '日' starts at byte position 0
U+672C '本' starts at byte position 3
U+8A9E '語' starts at byte position 6
//II. CONTROL FLOW
//1. for loop
for i := 0; i < 10; i++ {
sum += i //sum=sum+1
}
fmt.Println(sum)
sum := 1
for ; sum < 1000; {
sum += sum
}
fmt.Println(sum)
//u can leave the pre & post statements
sum := 0
for ; sum < 1000; {
sum += sum
}
fmt.Println(sum)
//this one will never complete, coz intially sum=0 and sum =sum+sum == 0+0, thus will always be zero
sum := 0
for sum < 1000 {
sum = sum+sum
}
fmt.Println(sum)
//for without anything else(except loop condition) acts like a while statement
for {
fmt.Println("cool")
}
//for loop without condition, loops forever
//2. if statement
func sqrt(x float64) string {
if x < 0 {
return sqrt(-x) + "i"
}
return fmt.Sprint(math.Sqrt(x))
}
func main() {
fmt.Println(sqrt(2), sqrt(-4))
}
import ("fmt"
"math"
)
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v //v is only available here
}
return lim //u cant access v here coz v is a var declared in the statement preceding the condition (v<lim) of 'if clause'
}
func main() {
fmt.Println(
pow(3, 2, 10)
)
}
//the if statement(like for loop ) can start with a short statement to execute before the CONDITION.
//vars declared byTHE STATEMENT are only in scope until the end of the if.
//those vars are however AVAILABLE in any else clauses of the if clause
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
} else {
//can use v here
fmt.Printf("%g >= %g\n", v, lim)
}
// can't use v here, though
return lim
}
//3. switch statement
import (
"fmt"
"runtime"
)
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.") //if os=='darwin' then this gets executed
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
}
//semicolons are to separate multiple statements on a line, should you write code that way.
//u could write line 83 as:
os := runtime.GOOS
switch os {
case "darwin": //etc
//switch without condition is like switch true
import (
"fmt"
"time"
)
func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 24:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}
//it's clean way to write long if-then-else chains.
//4. defer
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
//output "hello" \n "world"
//defer: defers the execution of a function until the surrounding function returns.
//it's usually good eg; in file operations;u can defer file closing to ensure the file is always closed after u r done operating with it.
//Deferred function calls are pushed onto a stack. When a function returns,
//its deferred calls are executed in last-in-first-out order.
func main() {
fmt.Println("counting")
for i := 0; i < 5; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
//output: "counting" "done", 4, 3, 2 .... //ie LIFO
//III. MORE TYPES; structs, slices, maps
//1. Pointers
func main() {
i := 42
p := &i // point to i
fmt.Println(*p) // 42
*p = 24 // set i through the pointer
fmt.Println(i) // 24
fmt.Println(p) // 0x1043617c
fmt.Println(i==*p) //true
y := *p/4 //u can do math with pointer as if its the real value
fmt.Println(y)
}
//pointers hold mem address of a value
//if p is a pointer of i, then value i == *p
package main
import "fmt"
func main() {
a := 1
b := 7
p := &a //pointer p==address of a
fmt.Println(p, *p) //0x1043617c 1
fmt.Println(a, &a) //1 0x1043617c
//ie p==&a; *p==a
*p = 3 //change value at addr &a to 3
p = &b //p == addr of b
//thus value of p==*p ==b ==7
//but value of a==3
fmt.Println("")
fmt.Println(p, *p) //0x10436180 7
fmt.Println(&a, a) //0x1043617c 3
}
//2. Structs
type Vertex struct {
X int
Y int
}
func main() {
c := Vertex{4,98}
fmt.Println(Vertex{1, 2})
fmt.Println(c)
fmt.Println(c.Y)
p := &c
p.X = 101
fmt.Println(c)
}
//struct is a collection of fields
//u access fields using dot, c.Y
//pointers work as expected
var (
v1 = Vertex{1, 2} // has type Vertex
v2 = Vertex{X: 1} // Y:0 is implicit
v3 = Vertex{} // X:0 and Y:0
p = &Vertex{1, 2} // has type *Vertex
)
func main() {
fmt.Println(v1, p, v2, v3)
}
//output: {1 2} &{1 2} {1 0} {0 0}
//The special prefix & returns a pointer to the struct value.
//3. Arrays
var a [10]int
//var a is an array of length 10
func main() {
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1]) // Hello World
fmt.Println(a) //[Hello World]
}
//arrays cannot be resized; length is part of their type
//The type [n]T is an array of n values of type T.
//arrays are inexflisible, so u dont see em often; instead u see slices
//4. Slices
func main() {
s := []int{2, 3, 5, 7, 11, 13}
fmt.Println("s ==", s)
for i := 0; i < len(s); i++ {
fmt.Println(i, s[i])
}
s[:3] //missing low index implies 0
s[2:] // missing high index implies len(s)
s[1:3]
s[1:1] //is empty
s[2:2+1] //has one element
}
//A slice points to an array of values and also includes a length.
//[]T is a slice with elements of type T.
//the slice type is an abstraction built on top of the array type
//slices can also be sliced further
//s[x:y] includes everything from x to y(excluding y)
//Making slices:
//they r made in either of two ways:
//(a) slicing an array (as seen above)
//(b) using make built-in fxn: func make([]T, len, cap) []T
b := make([]int, 0, 5) //len=0 & cap=5==optional param
len(b); cap(b) //fxns to find this values
//zero value of a slice is nil & len==cap==0
var z []int
fmt.Println(z, len(z), cap(z))
//Adding to slices:
//u can add using the builtin append fxn: http://golang.org/pkg/builtin/#append
//func append(slice []Type, elems ...Type) []Type
//it's necessary to store the result of append, often in the variable holding the slice itself
a := make([]int, 0) //equivalent to: var a []int //len param is manadatory, u cant do: make([]int)
a = append(a, 2, 3, 4)
fmt.Println(a) //output: [2 3 4]
a[2] = 45 //assign to that key
//passing in a slice to fxn
func showSlice(s string, myvar []int) {
fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x)
}
//NB: myvar []int
showSlice("a", a)
//Iterating slices:
var pow = []int{1, 2, 4, 8}
func main() {
for i, v := range pow {
fmt.Println(i, v)
}
}
//output: key, value
//If u only want the value; assign key to = _
for _, value := range pow {
fmt.Println(value)
}
//if u want only the key, use only one var
for key := range pow {
fmt.Println(key)
}
//NB: The Capacity of a slice is the max value the Length can reach.
//Trying to grow the slice beyond its capacity will step beyond the limits of the array and will trigger a panic.
//5. Maps
//They map keys to values. U must use make keyword to create 'em
//nil map is empty and cant be assigned into
type Vertex struct {
Lat, Long float64
}
func main() {
m := make(map[int]Vertex)
//m MAPS ints to things of type VERTEX
m[4] = Vertex{40.68433, -74.39967,}
fmt.Println(m[4])
//c := make(map[string]Vertex)
//c["coordinate"] = Vertex{2.33, -3.89,}
//fmt.Println(m["coordinate"])
}
//map literals r like struct literals except that the keys are required
type Komu struct {
Lat, Long float64
}
var m = make(map[string]Komu) //m maps strings to things of type Komu
func main() {
m["Bell Labs"] = Komu{40.68433, -74.39967,}
m["Google"] = Komu{37.42202, -122.08408,}
fmt.Println(m)
}
//the above declaration could also have bn done at once:
func main() {
m := map[string]Komu{
"Bell Labs": Komu{40.68433, -74.39967,},
"Google": Komu{37.42202, -122.08408,},
}
fmt.Println(m)
}
//////////////////////////////////////////////////////
//if top level type is just a type name, u can omit it from elements of th literal
type Vertex struct {Lat, Long float64}
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967}, //Vertex omitted here
"Google": {37.42202, -122.08408},
}
func main() { fmt.Println(m) }
//Mutating maps
//If u have a map, m
m[key] = elem //insert or update an element
elem = m[key] //Retrieve an element
delete(m, key) //Delete an element
elem, t = m[key] //test a key is present with a two-value assignment:
//if key exists elem==value at that key & t==true
//else; elem==zero value of the maps element type & t==false
m := make(map[string]int)
//m MAPS strings to ints
m["Answer"] = 42 //insert
m["Answer"] = 48 //update
delete(m, "Answer")
v, k := m["Answer"]
fmt.Println(v, k) //v==zero value of ints==0
//k==false
//6. More About Functions
//Functions are values too.
//so u can also assign them
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(3, 4))
}
//note that in this case the func has no name
func cool(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
} //u cant declare a fxn with a name (named fxn) inside main???**
func main() {
hypot := cool(3, 4)
fmt.Println(hypot)
}
//Function closures
//A closure is a FunctionValue(see above) that references variables from outside its body.
//The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables.
func adder() func(int) int { //returns a closure
sum := 0
return func(x int) int { //the closure, its bound to the sum variable
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
//ie: You can make the anonymous function close over a value, making it a closure.
valueToCloseOver := "My name is HAL."
anon := func(s name) string {
return "Hiya, " + name + ". " + valueToCloseOver
}
anotherFunction(anon)
//Regardless of where the anonymous function is passed to, it will always have access to var ‘valueToCloseOver’.
//This example is trivial. But passing logic and state around is a powerful coding mechanism.
func anotherFunction(f func(string) string) {
result := f("David")
fmt.Println(result) // Prints "Hiya, David"
}
//http://blog.denevell.org/golang-closures-anonymous-functions.html
//IV. METHODS AND INTERFACES
//1. Methods
//Go doesnt have classes; but u can defn methods on struct types.
func cool() int {
//code
//a normal/regular fxn called cool that takes no args and returns an int
}
type my_type struct { }
func (m my_type) cool_two() int {
//code
//a method called cool_two that takes no args, returns an int
//but is associated with a type we name my_type
}
type Vertex struct { X, Y float64 }
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y) //a method called Abs associated with *Vertex
}
func main() {
v := &Vertex{3, 4}
fmt.Println(v.Abs())
}
//You can declare a method on ANY TYPE that is DECLARED in YOUR package, not just struct types.
//u cant declare methods on a type from a diff package or even a builtin package
type MyFloat float64
func (f MyFloat) aha_fxn() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.aha_fxn())
}
//Methods can either be associated with a name type values or pointer to name type
//above Abs method is assoc with a pointer to a named type(*Vertex) while aha_fxn is assoc with name type(MyFloat)
//reasons to assoc with pointers are:
//(a) if the value type is a large struct, using pointers is more efficient
//(b) if u want the method to make modifications to the value that its receiver points to.
// NB: VERY IMPORTANT
// 1. If the receiver is a map, func or chan, use value receiver.
// 2. When in doubt, use a pointer receiver.
// https://github.com/golang/go/wiki/CodeReviewComments#receiver-type
// In general, all methods on a given type should be consistent and have either
// value or pointer receivers, but not a mixture of both.
type Vertex struct { X, Y float64 }
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
//you can treat the receiver(Vertex struct) as if it was an argument being passed to the method(Abs)
}
func main() {
v := &Vertex{3, 4}
v.Scale(5)
fmt.Println(v, v.Abs())
}
//the scale() method makes modifications to v, thus its better to use pointer
//for Abs() it doesnt modify v, so it doesnt matter if you assoc with pointer or name type
//2. Interfaces.
//It's a type that is defined by a set of methods.
//A value of interface type can hold any value that implements those methods.
type Abser interface {
Abs() float64
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser
// In the following line, v is a Vertex (not *Vertex)
// and does NOT implement Abser.
a = v //THIS WILL FAIL
fmt.Println(a.Abs())
}
//Interfaces are implemented explicitly
import ("os")
type Reader interface {
Read(b []byte) (n int, err error)
}
func main() {
var w Writer
// os.Stdout implements Writer
w = os.Stdout
fmt.Fprintf(w, "hello, writer\n")
}
//A type implements an interface by implementing the methods.
//One of the most common Interfaces is Stringer defined by the fmt package.
/*
type Stringer interface {
String() string
}
Stringer is a type that can describe itself as a string.
*/
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
fmt.Println(a)
}
//By convention, one-method interfaces are named by the method name plus an -er suffix
//3. Errors
//error is a builtin type similar to fmt.Stringer
type error interface {
Error() string
}
//fxns often return errors, and calling code shud check for that by comapring with nil
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
}
fmt.Println("Converted integer:", i)
//errors exercise
//create a custom error such that Sqrt should return a non-nil error value when given a negative number,
//such that ErrNegativeSqrt(-2).Error() returns "cannot Sqrt negative number: -2"
import (
"fmt"
)
//create a new type
//and make it an error by giving it a
//func (e ErrNegativeSqrt) Error() string
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negativ number: %v", float64(e)) //float64(e) needed to avoid infinite loop
}
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, ErrNegativeSqrt(f)
}
return 0, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
//4. Readers
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8) //u can replace 8 with len("Hello, Reader!")
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
fmt.Println(string(b)) //prints eader! R
//if b := make([]byte, len("Hello, Reader!")) then;
//fmt.Println(string(b)) == "Hello, Reader!"
}
//the io.Reader interface has a Read method:
//func (T) Read(b []byte) (n int, err error)
//Read populates the given byte slice with data and returns the number of bytes populated and an error value.
//It returns an io.EOF error when the stream ends.
//5. Web servers
//u can server HTTP requests using any value that implements http.Handler interface
type Handler interface {
ServeHTTP(w ResponseWriter, r *Request)
}
// remember that an inteface is satisified if all its methods** are implemented.
//so all u have to do is implement ServeHTTP() fxn and http.Handler will be satisified
import (
"fmt"
"log"
"net/http"
)
type Hello struct{}
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello!") //implement ServeHTTP() fxn thus satisfyin the http.Handler
}
func main() {
var h Hello
err := http.ListenAndServe("localhost:4000", h)
if err != nil {
log.Fatal(err)
}
}
//In this example, the type Hello implements http.Handler.
//webservers exercise: https://tour.golang.org/methods/14
//for this exercise we r 2Implement the following types and define ServeHTTP methods on them.
//and also register them to handle specific paths in our web server.
type Mystring string
type Mystruct struct {
Greeting string
Punct string
Who string
}
//answer:
package main
import (
"fmt"
"net/http"
)
type Mystring string
type Mystruct struct {
Greeting string
Punct string
Who string
}
func (s Mystring) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, s)
}
func (s Mystruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, s.Greeting, s.Punct, s.Who)
}
func main() {
var cool_str Mystring
cool_str = "I'm a frayed knot."
var cool_struct Mystruct
cool_struct = Mystruct{"Hello", ":", "Gophers!"}
// your http.Handle calls here
http.Handle("/string", cool_str)
http.Handle("/struct", &cool_struct)
http.ListenAndServe("localhost:4000", nil)
}
//from: http://golang.org/pkg/net/http/#Handle
func Handle(pattern string, handler Handler) //takes a string(uri) and a Handler
//remember that handler is anything that implements the Handler interface ie implements
// ServeHTTP() fxn
//also from: http://golang.org/pkg/net/http/#ListenAndServe
func ListenAndServe(addr string, handler Handler) error //takes an address as a string and a Handler an returns an error(if any)
//typically for this, Handler is usually nil
//its common to do:
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
//V. CONCURRENCY
//1. Goroutines
go f(x, y, z) //starts a new goroutine running f(x, y, z)
//The evaluation of f, x, y, and z happens in the current goroutine(the one handling main() fxn)
//and the execution of f happens in the new goroutine.
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(1 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello") //if u don't add this synchronous call here, main() fxn will execute b4 the
//go say goroutine has finished and thus exit. u could replace it with something like; time.Sleep(40 * time.Millisecond)
}
//output: hello world hello world etc
//goroutines run in the same address space, so access to shared memory must be synchronized. golang comes
//with a sync pkg that u can use to manage that. But u dont have to bcoz u probably ought 2b using channels
//another way to write the same program with synchronisation;
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func say(s string) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
wg.Done() //decrement no of goroutines to wait for
}
func main() {
wg.Add(1) //add no of gorutines to wait for
go say("world")
wg.Wait() //wait for the one goroutine 2finish execution
}
//SERIOUSLY THIS SYNCHRONIZATION AND RACE CONDITION THING CAN BE HARD
//USE CHANNELS.
//2. Channels
//Channels are a typed conduit through which you can send and receive values.
ch <- v // Send v to channel ch.
v := <-ch // Receive from channel ch & assign value to v.
ch := make(chan int) //Like maps and slices, channels must be created before use:
//By default, sends and receives block until the other side is ready.
//THIS ALLOWS goroutines to SYNCHRONIZE WITHOUT explicit LOCKS or condition variables.
func sum(a []int, c chan int) {
counter := 0
for _, v := range a {
counter = counter + v
}
c <- counter // send counter to c
}
func main() {
a := []int{7, 2, 8}
c := make(chan int) //make a channel of ints(it can only send/receive ints)
go sum(a, c)
x := <-c //receive from c and assign to x
fmt.Println(x)
}
//output: 17
//3. Buffered channels
ch := make(chan int, 100) //buffer length==100
//Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.
func main() {
c := make(chan int, 2)
c <- 1
c <- 45
//c <- 45 //this will thro err, coz c can only receive a max of two
fmt.Println(<-c)
fmt.Println(<-c)
//fmt.Println(<-c) //if u enable this, it'll thro error because c is a ch with buffer of 2
//and it has already sent all it had
}
//4. Channels, range & close
//A SENDER(and not receiver) can close a channel to indicate that no more values will be sent
//Receivers can test whether a channel has been closed
v, ok := <-ch
//ok is false if there are no more values to receive and the channel is closed.
//The loop for i := range c receives values from the channel repeatedly until it is closed.
func fibonacci(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 main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
//NB: chanels aren't like files(YOU DONT have to close them)
//Closing is only necessary when the receiver must be told there are no more values coming, such as to terminate a range loop
//5. Channels & select statement
//The select statement lets a goroutine wait on multiple communication operations.
//A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.
func fibonacci(c, quit chan int) { //two channels of ints
x, y := 0, 1
for { ////for loop bila condition, loops forever
select {
case c <- x: //send to c; start by sending x==0
x, y = y, x+y
case <-quit: //if a receive into channel quit is got, end fxn
fmt.Println("quit")
return
//default: //optional
//The default case in a select is run if no other case is ready.
}
}
}
var c = make(chan int)
var quit = make(chan int)
func cool() {
for i := 0; i < 4; i++ {
z := <-c //receive from channel c and assign to z
fmt.Println(z)
}
quit <- 0 //send 0 to channel quit
}
func main() {
go cool()
fibonacci(c, quit)
}
//output: 0, 1, 1, 2, quit
//if in fnx cool() we interchanged and sent to quit channel befor receiving from c channel
//the output would be: "quit" & nothing else
//this is coz fibonnanci is listening(waiting) for both conditions and whichever comes first is executed
//if case quit is got then return and end
//6. cancelling channels using context.
// gen is a broken generator that will leak a goroutine.
func gen() <-chan int {
ch := make(chan int)
go func() {
var n int
for {
ch <- n
n++
}
}()
return ch
} //gen starts a goroutine with an infinite loop, but the caller(below) consumes the values until n is equal to 5.
for n := range gen() {
fmt.Println(n)
if n == 5 {
break
}
}
// Once the caller is done with the generator (when it breaks the loop), the goroutine will run forever executing the infinite loop.
// THE FIX is
// gen is a generator that can be cancellable by cancelling the ctx.
func gen(ctx context.Context) <-chan int {
ch := make(chan int)
go func() {
var n int
for {
select {
case <-ctx.Done():
return // avoid leaking of this goroutine when ctx is done.
case ch <- n:
n++
}
}
}()
return ch
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // make sure all paths cancel the context to avoid context leak
for n := range gen(ctx) {
fmt.Println(n)
if n == 5 {
cancel()
break
}
}
// see: http://golang.rakyll.org/leakingctx/
//7. Pipelines
https://gist.github.com/claudiofahey/3afcf4f4fb3d8d3b35cadb100d4fb9b7
1. //What's the difference between concurrency and parallelism
- Concurrency is like having a juggler juggle many balls. Regardless of how it seems,
the juggler is only catching/throwing one ball at a time.
- Parallelism is having multiple jugglers juggle balls simultaneously.
- concurrency: u can apply for a passport and as u wait in line, u r wrting code, and as ua code compiles, u make a biz call etc
- parallelism: two of u are writing same code together
//a program running concurrently:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func cool1() {
for char := 'a'; char < 'a'+26; char++ {
fmt.Printf("%c ", char)
}
wg.Done() //announce u r done
}
func cool2() {
for number := 1; number < 27; number++ {
fmt.Printf("%d ", number)
}
wg.Done()
}
func main() {
wg.Add(2)
fmt.Println("Starting Go Routines")
go cool1()
go cool2()
fmt.Println("\nWaiting To Finish")
wg.Wait() //wait for goroutine 2finish execution
fmt.Println("\nTerminating Program")
}
//output:
Starting Go Routines
Waiting To Finish
a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Terminating Program
//more at: http://www.goinggo.net/2014/01/concurrency-goroutines-and-gomaxprocs.html
//if we made a small change
import "time"
func cool1() {
time.Sleep(1 * time.Microsecond)
for char := 'a'; char < 'a'+26; char++ {
fmt.Printf("%c ", char)
}
wg.Done()
}
//output is:
Starting Go Routines
Waiting To Finish
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 a b c d e f g h i j k l m n o p q r s t u v w x y z
Terminating Program
//we add a small sleep in first goroutine & this is enough to cause the scheduler to swap the two goroutines:
//about the WaitGroup: http://golang.org/pkg/sync/#WaitGroup
type WaitGroup struct {
// contains filtered or unexported fields
}
//A WaitGroup waits for a collection of goroutines to finish.
//The main goroutine(main fxn) calls Add to set the number of goroutines to wait for.
//Then each of the goroutines runs and calls Done when finished. [wg.Done() will decrements number of goroutine 2wait for]
//At the same time, Wait can be used to block until all goroutines have finished.
//Program running in parrallel
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
var wg sync.WaitGroup
func alphabets() {
for char := 'a'; char < 'a'+26; char++ {
fmt.Printf("%c ", char)
time.Sleep(time.Second * 1)
}
wg.Done() //decrement number of goroutines to wait for
}
func numbers() {
for number := 1; number < 27; number++ {
fmt.Printf("%d ", number)
time.Sleep(time.Second * 5)
}
wg.Done()
}
func main() {
runtime.GOMAXPROCS(2) //seems like this doen't affect much***
wg.Add(2) //wait for two goroutines
fmt.Println("Starting Go Routines")
go alphabets()
go numbers()
fmt.Println("\nWaiting To Finish")
wg.Wait() //wait for the two goroutines to finish executing
fmt.Println("\nTerminating Program")
}
//output:
Starting Go Routines
Waiting To Finish
a 1 b c d e 2 f g h i j 3 k l m n o 4 p q r s t 5 u v w x y 6 z 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Terminating Program
//look at concurrent_prog.go and parallel_prog.go in this same folder
2. //GOLANG TYPE SWITCH
//An interface{} type is a type that could be any value.
var x interface{} = "string"
var y interface{} = 123
//If you’re not sure about what the interface{} type could be, you can use a type switch:
switch v := x.(type) {
case string:
fmt.Println(v)
case int32, int64:
fmt.Println(v)
case *int:
fmt.Printf("pointer to integer %d\n", *t) // t has type *int
case SomeCustomType:
fmt.Println(v)
default:
fmt.Printf("unexpected type %T", t) // %T prints whatever type t has
}
3. //Allocation using make and new
//new(T) allocates zeroed storage for a new item of type T and returns a pointer to a newly allocated zero value of type T.
f := new(File) //type *File
//make(T, args) creates MAPS, SLICES, CHANNELS only, and it returns an initialized (not zeroed) value of type T (not *T).
4. //2D slices
a := [][]int{} //aslice of slices(and the slices are of ints)
row1 := []int{1, 2, 3}
row2 := []int{4, 5, 6}
a = append(a, row1)
a = append(a, row2)
fmt.Println(a) //[ [1 2 3] [4 5 6] ]
5. //Control default format of a custom type
//If you want to control the default format for a custom type,
//all thats required is to define a method with the signature String() string on the type.
//u however need 2convert the argument to the basic string type to prevent 'process took too long' error(because of infinite recursion)
type MyString string
func (m MyString) String() string {
return fmt.Sprintf("Bob=%s", string(m)) // OK: note conversion.
}
func main() {
var x MyString = "hello cool"
fmt.Println(x)
}
//this prints: Bob=hello cool
//instead of: hello cool
6. //in golang:
a << b == a* (2**b) //so;
4<<3 == 4*(2**3) == 4*8 == 32
7. //The init function
//each source file can define its own niladic init function to set up whatever state is required
func init() {
if user == "" {
log.Fatal("$USER not set")
}
if home == "" {
home = "/home/" + user
}
}
//- imported packages are initialized.
//- variable declarations in the package have evaluated their initializers
//- init is then called
//a file can have more than one init file
// func init() is discouraged. Have a look at, http://peter.bourgon.org/blog/2017/06/09/theory-of-modern-go.html
// You shouldn't have:
//- Package level variables
//- func init
8. //pointers vs values
func Append(slice, data []byte) []byte {
l := len(slice)
if l + len(data) > cap(slice) { // reallocate
// Allocate double what's needed, for future growth.
newSlice := make([]byte, (l+len(data))*2)
// The copy function is predeclared and works for any slice type.
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0:l+len(data)]
for i, c := range data {
slice[l+i] = c
}
return slice
}
//this is a small fxn that appends to slices.
//There's an inbuilt Append fxn that u should use; this is here for experment
//the problem is we are passing in slice and data as values(not pointers)
//so to modify slice, we have to return it for changes to take effect
//if we used pointers though, changes will take effect automatically and we dont have to return
type ByteSlice []byte
func (p *ByteSlice) Append(data []byte) {
slice := *p
// Body as above, without the return.
*p = slice
}
//we can do better, if we modfy it to loook like a standard Write method
func (p *ByteSlice) Write(data []byte) (n int, err error) {
slice := *p
// Again as above.
*p = slice
return len(data), nil
}
//then the type *ByteSlice satisfies the standard interface io.Writer, which is handy. For instance, we can print into one.
var b ByteSlice
fmt.Fprintf(&b, "This hour has %d days\n", 7)
//RULE: value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers.
9. //Interfaces
//we've seen a type implementing an interface(by implementing its methods)
//a type can also implement multiple interfaces (multiple inheritance??)
//we are going to implement a type that implements the sort.Interface and also a custom formatter
import(
"fmt"
"sort"
)
type Sequence []int
// Methods required by sort.Interface.
func (s Sequence) Len() int { return len(s) }
func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Method for printing - sorts the elements before printing.
func (s Sequence) String() string {
sort.Sort(s)
str := "["
for i, elem := range s {
if i > 0 {
str += " "
}
str += fmt.Sprint(elem)
}
return str + "]"
}
func main() {
x := make(Sequence, 0)
x= append(x, 33,14,5,65, 3,1)
fmt.Println(x)
}
//outputs: [1 3 5 14 33 65]
10. //Conversions
//we've implemented a string() method above for Sequence
//but we r just re-implementing the functionality of the inbuilt String method for slices
//so we can convert our sequence to slice of ints and take advantage of its String() without re-implementing it
func (s Sequence) String() string {
sort.Sort(s)
return fmt.Sprint([]int(s)) //conversion
}
func main() {
x := make(Sequence, 0)
x = append(x, 33, 14, 5, 65, 3, 1, 3)
fmt.Println(x)
}
//infact slices of ints already have an inbuilt sort fxn so all we've to do is convert Sequence to []int and call
//its builtin sort fxn
type Sequence []int
// Method for printing - sorts the elements before printing
func (s Sequence) String() string {
sort.IntSlice(s).Sort()
return fmt.Sprint([]int(s))
}
11 //Interfaces and Methods
//Since almost anything can have methods attached, almost anything can satisfy an interface
// Simpler counter server.
type Counter int
func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { //ResponseWriter is itself an interface
*ctr++
fmt.Fprintf(w, "counter = %d\n", *ctr)
} //handler to count the number of times the page is visited.
//receiver needs to be a pointer so the increment is visible to the caller
12. //Blank(_) identifier
//1. u can use it in multiple assignmenets if u dont need to use one of the vars
_, err := os.Stat(path);
//2. to silence compile errors for unused vars
var _ = []int{2,3,7} //no errors even if u dont use this var
//3. unused imports
import _ "net/http/pprof" //when u just want to import a package
//for its side effects(eg its init fxn)
13. //Embedding
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
Reader
Writer
}
14.//logging
package main
import (
_ "io"
"log"
"os"
)
func main() {
Trace := log.New(os.Stdout, "TRACE: ", log.Ldate|log.Ltime|log.Lshortfile)
Trace.Println("I have something standard to say")
//func New(out io.Writer, prefix string, flag int) *Logger
}
//or simpler
func main() {
log.Println("hello")
}
//back to embedding in no 13;
type Job struct {
Command string
*log.Logger //embedded field alonside a regular field
} //embedded field dont have to be named
func main() {
mylogger := log.New(os.Stdout, "TRACE: ", log.Ldate|log.Ltime|log.Lshortfile)
job := Job{"hi", mylogger}
job.Println("Yo")
}
f, err := os.OpenFile("testlogfile", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
defer f.Close()
log.SetOutput(f)
log.Println("This is a test log entry")
//log to file
15. //Goroutines
//it's a fxn executing concurrently with other goroutines in the same address space
//they'r multiplexed onto multiple OS threads so if one should block, others continue to run
go list.Sort() //run concurrently; don't wait for it. when call completes goroutine exits, silently
func Announce(message string, delay time.Duration) {
go func() {
time.Sleep(delay)
fmt.Println(message)
}() // Note the parentheses - must call the function.
} //execute goroutine as fxn literal
//this goroutiines have no way of signaling completion; thats why we need channels
//goroutines are multiplexed onto multiple os threads.
//If one goroutine blocks an OS thread, other goroutines in this thread will migrate so that they may continue running.
//You do not have to worry about these details.
16. //Channels
//A channel can allow the launching goroutine to wait for a computation to complete.
func main() {
c := make(chan int) // Allocate a channel.
go func() { // Start the sort in a goroutine; when it completes, signal on the channel.
time.Sleep(100000); fmt.Println("iii") //COMPUTATION
c <- 1 // Send a signal; value does not matter.
}()
<-c // Wait for sort to finish; discard sent value.
}
//Receivers block until there is data to receive.
//sender blocks until the receiver has received the value.
//ie (sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.)
//Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.
17. //Errors
//errors have type error, a simple built-in interface.
type error interface {
Error() string
}
//a library developer is free to implement this interface and even provide more context
//eg os.Open returns an error(nil if none); and this error is internally implemented as;
type PathError struct {
Op string // "open", "unlink", etc.
Path string // The associated file.
Err error // Returned by the system call.
}
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}
//example output: open /etc/passwx: no such file or directory
18. //Panic
//sometimes we don't wont to just throw an error, we wont the program to exit at the error.
//panic is a fxn that takes a single argument of arbitrary type(often a string) to be printed as the program dies.
var user = os.Getenv("USER")
func init() {
if user == "" {
panic("no value for $USER")
}
}
//when panic is called: it stops execution of current fxn & runs any deferred functions along the way
19. //Recover
//a call to recover stops the unwinding of panic and returns the argument passed to panic.
package main
import "fmt"
func yo(x int){
if x < 0 {
panic("ha ha")
}
}
func main(){
defer func(){
if err:=recover();err != nil {
fmt.Println(err)
}
}() //this is an anonymous fxn, SO IT MUST BE CALLED; hence paranthesis
yo(-8)
}
//output: ha ha
18. //some OO stuff
package main
import "fmt"
type Person struct {
Name string
Address Address //some sort of inheritance
}
type Address struct {
BoxNumber string
City string
}
func (p *Person) Talk() {
fmt.Println("Hi, my name is", p.Name)
}
func (p *Person) Location() {
fmt.Println("I live at", p.Address.BoxNumber, p.Address.City)
}
func main() {
addr := Address{
BoxNumber: "13",
City: "Gotham",
}
p := Person{
Name: "Steve",
Address: addr,
}
p.Talk()
p.Location()
} //Hi, my name is Steve //I live at 13 Gotham
19. //Embedded Types contd
//A struct's fields usually represent the has-a relationship. For example a Circle has a radius. Suppose we had a person struct:
type Person struct {
Name string
}
func (p *Person) Talk() {
fmt.Println("Hi, my name is", p.Name)
}
//And we wanted to create a new Citizen struct. We could do this:
type Citizen struct {
Person Person
country string
}
//This would work, but we would rather say a Citizen is a Person, rather than an Citizen has a Person. Go supports relationships like this by using an embedded type. Also known as anonymous fields, embedded types look like this:
type Citizen struct {
Person
country string
}
//We use the type (Person) and don't give it a name. When defined this way the Person struct can be accessed using the type name:
a := new(Citizen)
a.Person.Talk()
//But we can also call any Person methods directly on the Android:
a := new(Android)
a.Talk()
20. //Iterating bug
package main
import "fmt"
func main() {
strings := []string{"name","Komu"}
for _, value := range strings {
fmt.Printf("Value: %s and Pointer is: %v\n", value, &value)
}
}
//output:
//Value: name and Pointer is: 0x1040a120
//Value: Komu and Pointer is: 0x1040a120
//you can see the pointer, remained the same for each iteration(0x1040a120)
//to fix that u shud use a temp variable
for _, value := range strings {
tmp := value
fmt.Printf("Value: %s and Pointer is: %v\n", tmo, &tmp)
}
//works
//u should never use the pointer(address) of an iterated variable
21. //Shadowing
package main
import "fmt"
func main() {
x := 1
fmt.Println(x) //prints 1
{
fmt.Println(x) //prints 1
x := 2
fmt.Println(x) //prints 2
}
fmt.Println(x) //prints 1 (bad if you need 2)
}
//use go vet to see shadowed vars
$ go tool vet -shadow your_file.go
22. // Nice notes when reviewing golang code(code review)
https://github.com/golang/go/wiki/CodeReviewComments
23. // Golang scheduler and net poller
```bash
Currently, there are operating system threads and there is a set, of size
GOMAXPROCS, of "being able to run a goroutine."
Threads move in and out of the set based on what the goroutines they are executing do.
When thinking about the Go scheduler it helps to think about goroutines and threads separately,
although goroutines always execute on threads.
Let's say that a goroutine executes some system call that does not complete immediately.
When it enters the system call it does not know it is going to block.
What happens today (and it has been different in the past) is that there is a system monitor thread (not goroutine)
that wakes up every so often (the wakeup frequency depends on various things).
The system monitor thread will check on all the executing goroutines.
It will observe that one of the goroutines entered a system call some time ago but has not come out, so it is presumably blocked.
Because goroutines run on threads, this of course means that the thread is blocked in a system call.
The system monitor thread takes away that thread's "right to run a goroutine" and grants it to a different thread (creating a new thread if necessary).
That other thread will look at the list of runnable goroutines, and start executing one.
When the thread/goroutine blocked in the system call return from the call, the thread will try to reacquire the "right to run a goroutine;"
if it fails, the goroutine will be put on the queue of goroutines waiting to run, and the thread will go to sleep until
the system monitor thread wakes it up.
This is all complicated by the concurrent garbage collector, but probably not in a relevant way.
``` - Ian Lance Taylor, https://groups.google.com/forum/#!topic/golang-dev/HJcGESXfJfs
Also have a look at; https://rakyll.org/scheduler/
/*
Golang gotchas:
greate read: http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/
*/
1. //Shadowing
package main
import "fmt"
func main() {
x := 1
fmt.Println(x) //prints 1
{
fmt.Println(x) //prints 1
x := 2
fmt.Println(x) //prints 2
}
fmt.Println(x) //prints 1 (bad if you need 2)
}
//use go vet to see shadowed vars
$ go tool vet -shadow your_file.go
2. //Iterating bug
package main
import "fmt"
func main() {
strings := []string{"name","Komu"}
for _, value := range strings {
fmt.Printf("Value: %s and Pointer is: %v\n", value, &value)
}
}
//output:
//Value: name and Pointer is: 0x1040a120
//Value: Komu and Pointer is: 0x1040a120
//you can see the pointer, remained the same for each iteration(0x1040a120)
//to fix that u shud use a temp variable
for _, value := range strings {
tmp := value
fmt.Printf("Value: %s and Pointer is: %v\n", tmo, &tmp) //works
}
//u should never use the pointer(address) of an iterated variable
3. //Strings are immutable
// Strings are read-only byte slices(with a few extra properties)
func main() {
x := "text"
x[0] = 'T'
fmt.Println(x)
} //COMPILE error
func main() {
x := "text"
xbytes := []byte(x)
xbytes[0] = 'T'
fmt.Println(string(xbytes)) //WORKS
}
4. //strings and indices
func main() {
x := "text"
fmt.Println(x[0]) //print 116 rather than 't'
//116 is the codepoint for 't'
}
func main() {
x := "text"
fmt.Println( string(x[0]) )
} // prints 't'
5. //string length
data = u'♥'
print(len(data)) //prints: 1 in python
func main() {
data := "♥"
fmt.Println(len(data)) //prints: 3
}
//len() function returns the number of bytes instead of the number of characters
import (
"fmt"
"unicode/utf8"
)
data := "♥"
fmt.Println(utf8.RuneCountInString(data)) //prints 1
6. //log.Fatal and log.Panic abort app
import "log"
func main() {
log.Fatalln("Fatal Level: log entry") //app exits here
log.Println("Normal Level: log entry") //not called
}
7. //Value of iteration keys in strings
//The index value (the first value returned by the "range" operation) is the index of the first byte for the
//current "character" (unicode code point/rune) returned in the second value. It's not the index for the current "character"
8. //Unexported(lowercase) Struct Fields Are Not Encoded
import (
"fmt"
"encoding/json"
)
type MyData struct {
One int
two string //lowercase(unexported)
}
func main() {
in := MyData{1,"two"}
fmt.Printf("%#v\n",in) //prints main.MyData{One:1, two:"two"}
encoded,_ := json.Marshal(in)
fmt.Println(string(encoded)) //prints {"One":1}
}
9. //closing http response body
//when u make http request, u need to close the http response body
func main() {
resp, err := http.Get("https://api.ipify.org?format=json")
defer resp.Body.Close()//not ok
if err != nil {
fmt.Println(err)
return
}
//blah blah
//if req fails, resp==nil and defer resp.Body.Close() will cause a runtime panic
resp, err := http.Get("https://api.ipify.org?format=json")
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()//ok, most of the time :-)
//when you get a redirection failure both resp and err variables will be non-nil
//thus defer resp.Body.Close() will not even be called**
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
resp, err := http.Get("https://api.ipify.org?format=json")
if resp != nil {
defer resp.Body.Close() //FIX
}
if err != nil {
fmt.Println(err)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
10. //closing http connections
//some servers keep network connections open for a while (based on their keep-alive configs)
//the standard http library will close the network connections only when the target HTTP server asks for it
//thus your app may run out of sockets/file descriptors under certain conditions.
//1. u can ask it close the connection after your request is done by setting the Close field in the request variable to true.
//2. or u can add a Connection request header and set it to close
//3. or create a custom http transport configuration that disables http connection reuse globally
//If you send a lot of requests to the same HTTP server it's ok to keep the network connection open
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
req, err := http.NewRequest("GET","http://golang.org",nil)
if err != nil {
fmt.Println(err)
return
}
req.Close = true //method 1
//or do this:
//req.Header.Add("Connection", "close") //method 2
resp, err := http.DefaultClient.Do(req)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
fmt.Println(err)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(len(string(body)))
}
func main() {
tr := &http.Transport{DisableKeepAlives: true} //method 3
client := &http.Client{Transport: tr}
resp, err := client.Get("http://golang.org")
if resp != nil {
defer resp.Body.Close()
}
11. //recovering from panic
//The recover() function can be used to catch/intercept a panic. Calling recover() will do the trick only when it's done (called DIRECTLY) in a deferred function.
func main() {
recover() //doesn't do anything
panic("not good")
recover() //won't be executed :)
fmt.Println("ok")
}
func main() {
defer func() {
fmt.Println("recovered:",recover()) //WORKS
}()
panic("not good")
}
12. //type declarations and method inheritance
//When you create a type declaration by defining a new type from an existing (non-interface) type,
//you don't inherit the methods defined for that existing type.
import "sync"
type myMutex sync.Mutex
func main() {
var mtx myMutex
mtx.Lock() //error
}
//If you do need the methods from the original type you can define a new struct type embedding the original type as an anonymous field.
type myLocker struct {
sync.Mutex
}
func main() {
var lock myLocker
lock.Lock() //ok
lock.Unlock() //ok
}
13. //breaking out of for-switch and for-select statements
//A "break" statement without a label only gets you out of the inner switch/select block.
//If using a "return" statement is not an option then defining a label for the outer loop is the next best thing.
func main() {
loop:
for {
switch {
case true:
fmt.Println("breaking out...")
break loop //this will break of of both inner(switch) and outer(for) loops
}
}
fmt.Println("out!")
}
// DOC: https://github.com/alco/gostart
/*
Anything you import is relative to your $GOPATH/src. If you have a dir like:
$GOPATH/src/
└github.com/komuw/
└── gopher
├── main.go
└── sub
└── sub.go
in your `main.go` file you'll need to import `sub` as follows:
*/
package main
import "github.com/komuw/gopher/sub"
func main() {
sub.ExportedFunction()
}
// To keep it simple, ALWAYS create a new directory inside $GOPATH/src/github.com/komuw/ when you're starting a new Go project.
1. Splitting package into multiple files:
/*
Go treats files in a single dir as belonging to one package
as long as they all have the same name in their package declarations.
`$ cd $GOPATH/src/github.com/komuw/example`
`$ cat helper.go`
*/
package main
func privateHelperFunc() int {
return 25
}
/*
# now edit main.go to call this function
`$ cat main.go`
*/
package main
// we do not need to import helper.go since it belongs to same package as main.go, ie pkg `main`
func main() {
fmt.Println("Hello world! My lucky number is", privateHelperFunc())
}
/*
We cannot simply `go run main.go` because `main.go` calls a function defined in another file.
We can either;
- pass all files as arguments to go run or
- build the current package and then run the produced binary.
*/
`$ go run *.go //or`
`$ go build`
`$ ./example`
2. Splitting package into multiple subpackages:
/*
Use a subpackage only when its functionality is tied to the main package which contains it
`$ cd $GOPATH/src/github.com/komuw/example`
`$ mkdir math`
`$ cat math/math.go`
*/
package math
func Mul2(x int) int {
return x * 2
}
/*
`$ cat $GOPATH/src/github.com/komuw/example/main.go`
*/
package main
import (
"fmt"
"github.com/komuw/example/math" // just "math" would not work, it would import std package math
)
func main() {
fmt.Println("number is", math.Mul2(privateHelperFunc()))
}
/*
`$ go run *.go`
*/
/*
Note that if you were to replace package math in `math.go` with package main,
this would not make math.go part of the same main package that both `main.go` and `helper.go` belong to.
It would be treated as another main package and you would get an error when trying to run or import it because it doesn't define a main function.
*/
3. Create a package for others to use (i.e. a non-main package):
/*
Packages that declare `package main` and are meant to be built to produce an executable binary are usually called `commands`.
The other flavor of Go packages is used as libraries or modules.
You can't build them into an executable.
Their purpose is to be imported into another package (not necessarily main package) to provide functionality to that package.
You create a package the same way as you would create a command.
The only difference is that instead of package main you write `package some_name`
*/
/*
`$ cd $GOPATH/src/util`
`$ cat cool.go` // file name is arbitrary and doesn't make any significance to Go
*/
package util
import "math"
func Square(x float32) float32 {
return x * x
}
/*
`$ go build`
This produces no output, it is only used to ensure it can compile with no errors
To precompile the package for use by other package,
you run `go install`.
This will put `util.a` into `$GOPATH/pkg/<arch>/util.a`
When building (`go build`) a main package, the resulting executable will be placed in the current directory.
Running go install, on the other hand, will build the source as usual and place the executable in $GOPATH/bin.
Running `go build` inside the directory of a simple package does not produce any binary.
Building a package this way is used to verify that it compiles cleanly.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment