Gopherlabs - the Ultimate Workshop Track 

gopherlabs.kubedaily.com

Chapter -1 : Golang Basics 

Introduction to variables 

Declare Variable 

package main
import "fmt"
var a int
func  main ()  {

	fmt.Println(a)

}
package main

import "fmt"

func main() {

    var i int = 1
    var w float64 = 12.5

    fmt.Println(i, w)
}

Short Variable Declaration 

// of := (short declaration
// operator)
package main

import "fmt"

func main() {

	// declaring and initializing the variable
a := 30

	// taking a string variable
Language := "Go Programming"

	fmt.Println("The Value of a is: ", a)
	fmt.Println("The Value of Language is: ", Language)

}

Declare multiple Variable 

package main

import (
    "fmt"
)

func main() {

    var i, j, k = 1, 2, 3

    var (
        name       = "Sangam Biradar"
        occupation = "Developer Advocate"
    )

    fmt.Println(i, j, k)
    fmt.Printf("%s is a %s\n", name, occupation)
}

Go type inference

package main

import (
    "fmt"
    "reflect"
)

func main() {

    var name = "Sangam"
    var age = 25

    fmt.Println(reflect.TypeOf(name))
    fmt.Println(reflect.TypeOf(age))

    fmt.Printf("%s is %d years old\n", name, age)
}

Go can infer the data type from the right side of the assignment.

package main

import "fmt"

var word string = "Learning"

func main() {

    i := 12

    fmt.Println(word)
    fmt.Println(i)

    test()
}

func test() {

    fmt.Println(word)
}

Scope of Variable 

path separator 

func Split(path string ) (dir file string)

path.split

demo/go-workshop.pdf

demo/

go-workshop.pdf

package main

import (
	"fmt"
	"path"
)
func main() {
	var file string
	_, file = path.Split("css/main.css")
	fmt.Println("file",file)
}

When to use short variable

declaration ?

use declaration :

1. if you don't know the initial value 

2. When you need package scope variable 

3. when you want to group variable

together  for great readability 

package main

func main() {
	score := 0 //Don't
}
package main

func main() {
	// score := 0 //Don't
	var score int  // already 0
}
package main

// version := o 
var version := 0
func main() {
	// score := 0 //Don't
	var score int  // already 0
}
package main

// version := o
var version := 0
func main() {
	// score := 0 //Don't
	var score int  // already 0

	var (
		// related
		video string

		// Closely related
		 duration int
		current int
	)
}

Use Short Declaration  

if you know the initial value 

package main

func main() {
	// var width, height = 100 , 50 // DONT !
	width , height := 100,50 
}

keep code concise

for reclaration

package main

func main() {
	// var width, height = 100 , 50 // DONT !
	width , height := 100,50

	width = 50 // assigns 50 to width 
	color := "red" // new variable: color
}
package main

func main() {
	// var width, height = 100 , 50 // DONT !
	width , height := 100,50

	// DONOT
	// width = 50 // assigns 50 to width
	// color := "red" // new variable: color
	 width , color = 50 , "red"
	
	
}

Convert value !

Type Conversion 

changes the type of a value to another type

type(value)

name of the type

changes the type of a given value into this type

value 

to be converted 

you can't use values belongs to different types  together 

package main

func main() {

  speed := 100  // speed is int 
  force := 2.5  // force is float64

  speed = speed * force
  
}
 // compiler error 
 
 speed * force 
 mismatched type int and float64
package main

func main() {

  speed := 100  // speed is int 
  force := 2.5  // force is float64

  speed = speed * int (force)
  
 fmt.Println(speed)
 fmt.Println(force , int(force))
  
}

// 200

2.5 becomes 2 

conversion can be a destructive operation

force variable's value

is still 2.5

func main {
 speed := 100 // speed is int 
 force := 2.5 //force is float64
 speed = int ( float64( speed ) * force )
 fmt.Println(speed)
 }

float64(100) = 100.0 

get input form the command line 

OS PACKAGE

os = operating system

package 

OS

allows you to access to operating system functionalities

INTRO TO SLICES 

Args variable belongs to the OS package

var Arg []string

when you run a Go program 

Go puts the command-line arguments into this variable automatically 

Args's type is a

"slice of strings"

Args slice can store multiple string values

each value inside slice is an unnamed variable

go run main.go hi yo
go run main.go hi yo
 Args[0]    Args[1]   Args[2]

index expression

path to the program 

../exe/main

First Argument

second Argument

Basic of OS.Args 

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Printf("%#v\n", os.Args)
}
package main
import (
	"fmt"
     "os"
)
func main() {
  fmt.Printf("%#v\n",os.Args)

  fmt.Println("Path", os.Args[0])
  fmt.Println("1st argument",os.Args[1])
  fmt.Println("2nd argument",os.Args[2])
  fmt.Println("3rd argument",os.Args[3])
  fmt.Println("number of items inside os.Args:",len(os.Args))
}

Basic Strings 

Raw String

Literal

Concatenation

string 

Length

project

Type

string

"hi there ! "

string literal

string

`hi

there`

raw string literal

single line

multi-line

package main

import "fmt"

func main() {

	var s string
	s = "how are you?"
	s = `how are you?`
	fmt.Println(s)

	s = "<html> \n\t <body> \"hello\"</html>"
	s = `
<html>
<body> "hello" </body>
</html> `
	fmt.Println(s)
	fmt.Println("c:\\my\\dir\\file")
	fmt.Println(`c:\my\dir\file`)
}

String Length

get the length of a string value

package main

import "fmt"

func main() {
  name := "sangam"
   fmt.Println(len(name))
}

len(stringValue)

length of string

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
  name := "संगम"
   fmt.Println(
   utf8.RuneCountInString(name))
}
package main

import (
	"fmt"
	"os"
	"strings"
)

func main(){

	msg := os.Args[1]
	l := len(msg)
	s := msg + strings.Repeat("!",l)
	fmt.Println(s)

}
package main

import (
	"fmt"
	"os"
	"strings"
)

func main(){

	msg := os.Args[1]
	l := len(msg)
	s := msg + strings.Repeat("!",l)
	s = strings.ToUpper(s)
	fmt.Println(s)

}

IOTA

build-in constant number generator


import "fmt"

func main(){

	const (
		monday = 0
		tuesday = 1
		wednesday = 2
		thursday = 3
		friday = 4
		saturday = 5
		sunday = 6

	)

fmt.Println(monday,tuesday,wednesday,thursday,friday,saturday,saturday,sunday)
}
package main

import "fmt"

func main(){

	const (
		monday = iota
		tuesday
		wednesday
		thursday
		friday
		saturday
		sunday

	)

fmt.Println(monday,tuesday,wednesday,thursday,friday,saturday,saturday,sunday)
}

iota is a built-on constant generator which generates ever increasing number

package main

import "fmt"

func main(){

	const (
		monday = iota + 1
		tuesday
		wednesday
		thursday
		friday
		saturday
		sunday

	)

fmt.Println(monday,tuesday,wednesday,thursday,friday,saturday,sunday)
}

Blank - Identifier with iota

package main

import "fmt"

func main(){

	const (
		EST = -(5 + iota)
		_
		MST
		PST

	)
	fmt.Println(EST,MST,PST)

fmt.Println()
}

Printf 

prints formatted output 

Printf

Escape 

Sequences

Printing 

Types

Examples

func main(){

var brand = "Google"
fmt.printf("%q\n",brand)
}
func main(){

var brand string 
fmt.printf("%q\n",brand)
}
func main(){

var brand = "Google"
fmt.printf("%s\n",brand)
}
fmt.Printf("%q\n",brand)

formatting text

replacer value(s)

replaces the verbs inside the formatting text

fmt.Printf(
"total:%d  success:%d  %d \n",
ops, ok , fails,
)

escape sequences

func main(){

fmt.println("hihi")
}
func main(){

fmt.println("hi\nhi")
}
      %b	base 2
      %c	the character represented by the corresponding Unicode code point
      %d	base 10
      %o	base 8
      %q	a single-quoted character literal safely escaped with Go syntax.
      %x	base 16, with lower-case letters for a-f
      %X	base 16, with upper-case letters for A-F
      %U	Unicode format: U+1234; same as "U+%04

if statement

if  score > 3 {
fmt.Println("good")
}
if (score > 3) {
fmt.Println("good")
}
if  score > 3 && valid == true {
fmt.Println("good")
}
if  score > 3 && valid {
fmt.Println("good")
}
func main()
{
score , valid := 3, true 
if score > 3 && valid{
fmt.Println("good")
}
}

else and else if 

package main

import "fmt"

func main() {
	score := 2

	// score, valid := 3, true
	if score > 3 { // && valid
		fmt.Println("good")
	} else if score == 3 {
		fmt.Println("on the edge")
	} else if score == 2 {
		fmt.Println("meh...")
	} else {
		fmt.Println("low")
	}
}

what is a nil value ?

nil value means that the value is not initialized yet  

err := do()

if err != nil {
  // x error 
  // handle it !
  // terminate ! 


}

nil

error 

value

error 

handling

error value

strconv.itoa never fails

func Itoa(i int) string 
package main

import (
	"fmt"
	"strconv"
)

func main() {

	s := strconv.Itoa(42)
	fmt.Println(s)

}

strconv.Atoi sometimes fails , so you've to handle the error 

func Itoa(s string) (int , error)

return a value with an error type

package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {

	n, error := strconv.Itoa(os.Args[1])
	fmt.Println("Converted Number :", n)
	fmt.Println("Returned error value :", err)

}

error handling

package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {

	arg := os.Args[1]
	n, err := strconv.Atoi(age)
	if err != nil {

		fmt.Println("ERROR:", err)
		return

	}
	fmt.Printf("SUCCESS : Converted %q to %d \n", age, n)

}

simple statement

short if

Scopes

Shadowing

short if

package main

import (
	"fmt"
	"strconv"
)

func main() {

	n, err := strconv.Atoi("42")

	if err == nil {
		fmt.Println("there was no error , n is ", n)
	}
}
package main

import (
	"fmt"
	"strconv"
)

func main() {

	if n, err := strconv.Atoi("42"); err == nil {
		fmt.Println("there was no error , n is ", n)
	}
}

simple statements allow you to execute a statement inside another statement

scope

package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {

	if a := os.Args; len(a) != 2 {

		// only : a variable
		fmt.Println("Give me a number.")
	} else if n, err := strconv.Atoi(a[1]); err != nil {
		// only : a, n and err variable
		fmt.Printf("cannot convert &q. \n", a[1])

	} else {
		// all the variable in the if statements
		fmt.Printf("%s * 2 %d\n", a[1], n*2)
	}
}

shadowing 

package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	var n int 

	if a := os.Args; len(a) != 2 {

		// only : a variable
		fmt.Println("Give me a number.")
	} else if n, err := strconv.Atoi(a[1]); err != nil {
		// only : a, n and err variable
		fmt.Printf("cannot convert &q. \n", a[1])

	} else {
		// all the variable in the if statements
		fmt.Printf("%s * 2 %d\n", a[1], n*2)
	}

	fmt.Printf("n is %d - You've been shadowed ;)\n", n)
}
package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	var (
		n int

		err error
	)

	if a := os.Args; len(a) != 2 {

		// only : a variable
		fmt.Println("Give me a number.")
	} else if n, err = strconv.Atoi(a[1]); err != nil {
		// only : a, n and err variable
		fmt.Printf("cannot convert &q. \n", a[1])

	} else {
		n *= 2
		// all the variable in the if statements
		fmt.Printf("%s * 2 %d\n", a[1], n)
	}

	fmt.Printf("n is %d - You've been shadowed ;)\n", n)
}

the switch  statement 

package main

import "fmt"

func main() {
	city := "Chandigarh "

	switch city {

	case "Chandigarh ":
		fmt.Println("india")

	}

}

case clause

package main

import "fmt"

func main() {
	city := os.Arg[1]

	switch city {

	case "Chandigarh":
		fmt.Println("India")
	case "tokyo":
		fmt.Println("japan")

	}

}

Default Clause

package main

import "fmt"

func main() {
	city := "limbo "

	switch city {

	case "Chandigarh ":
		fmt.Println("india")
    default: 
    fmt.Println("where?")
        

	}

}

multiple case conditions 

package main

import "fmt"

func main() {
	city := "Chandigarh  "

	switch city {

	case "Chandigarh ", "delhi":
		fmt.Println("india")
	default:
		fmt.Println("where?")

	}

}

Bool Expressions

package main

func main() {

	switch true {

	case i > 0:
	// "positive"
	
	case i < 0:
	// "negative"
	default:
	// "zero"

	}

}
package main

import "fmt"

func main() {

	i := 10

	switch {

	case i > 0:
		fmt.Println("positive")

	case i < 0:
		fmt.Println("negative")
	default:
		fmt.Println("zero")

	}

}

fall through statement 

package main

import "fmt"

func main() {

	i := 142

	switch {
	case i > 100:
		fmt.Print("big ")
		fallthrough
	case i > 0:
		fmt.Print("positive ")
		fallthrough
	default:
		fmt.Print("number")
	}
}

short switch 

package main

import "fmt"

func main() {

	switch i := 10; {
	case i > 0:
		fmt.Print(" possitive ")

	case i < 0:
		fmt.Print(" negative ")

	default:
		fmt.Print("zero")
	}
}

there is only one loop statement in Go 

for statement

for 

statement

Nested

Loops

Range 

Loops

package main

import "fmt"

func main() {
	var sum int
	sum++
	sum += 2
	sum += 3
	sum += 4
	sum += 5

	fmt.Println(sum)
}
package main

import "fmt"

func main() {
	var sum int
	for i := 1; i <= 10; i++ {
		sum += i

	}
	fmt.Println(sum)
}

for range

easy way to loop over slices

package main

import (
	"fmt"
	"os"
)

func main() {
	for _, v := range os.Args[1:] {
		// if i == 0 {
		// continue
		// }
		fmt.Printf("%q\n", v)

	}

}

Arrays 

collection of elements 

indexable 

Fixed Length

 

Slice

collection of elements 

indexable 

Dynamic Length

 

String Internals

bytes slices 

ASCII & Unicode

Encoding & Decoding

 

Maps 

Collection 

of Indexable 

Key-value Pairs

 

Struct 

Groups 

different types of variable together

Chapter -2 : Array & Memory Layout  

 

Arrays

  • What is an array
  • Getting and Setting Array elements 
  • Array Literals - Easy way to create arrays 
  • Comparing Arrays 
  • Assigning arrays 
  • Multi-Dimension arrays 
  • Keyed Elements 
  • Named VS Unnamed Types 

What is array ?

Think of this grid like the computer memory

a single memory cell

1 byte wide

var myAge byte
var herAge byte
package main

import "fmt"

const (
	winter = 1
	summer = 3
	yearly = winter + summer
)

func main() {
	// var books [4]string
	// var books [1 + 3]string
	var books [yearly]string

	books[0] = "Kafka's Revenge"
	books[1] = "Stay Golden"
	books[2] = "Everythingship"
	books[3] += books[0] + " 2nd Edition"


	// Go compiler can catch indexing errors when constant is used
	// books[4] = "Neverland"

	// Go compiler cannot catch indexing errors when non-constant is used
	// i := 4
	// books[i] = "Neverland"

	// --------------------
	// PRINTING ARRAYS
	// --------------------

	// print the type
	fmt.Printf("books  : %T\n", books)

	// print the elements
	fmt.Println("books  :", books)

	// print the elements in quoted string
	fmt.Printf("books  : %q\n", books)

	// print the elements along with their types
	fmt.Printf("books  : %#v\n", books)
}
package main

import "fmt"


const (
	winter = 1
	summer = 3
	yearly = winter + summer
)

func main() {
	var books [yearly]string

	books[0] = "Kafka's Revenge"
	books[1] = "Stay Golden"
	books[2] = "Everythingship"
	books[3] += books[0] + " 2nd Edition"
	fmt.Printf("books  : %#v\n", books)


	var (
		wBooks [winter]string
		sBooks [summer]string
	)

	// assign the first book as the wBook's first book
	wBooks[0] = books[0]

	// assign all the books starting from the 2nd book
	// to sBooks array

	// sBooks[0] = books[1]
	// sBooks[1] = books[2]
	// sBooks[2] = books[3]

	// for i := 0; i < len(sBooks); i++ {
	// 	sBooks[i] = books[i+1]
	// }

	for i := range sBooks {
		sBooks[i] = books[i+1]
		// changes to sBooks[i] will not be visible here.
		// sBooks here is a copy of the original array.
	}
	// changes to sBooks are visible here

	// sBooks is a copy of the original sBooks array.
	//
	// v is also a copy of the original array element.
	//
	// if you want to update the original element, use it as in the loop above.
	//
	// for _, v := range sBooks {
	// 	v += "won't effect"
	// }

	fmt.Printf("\nwinter : %#v\n", wBooks)
	fmt.Printf("\nsummer : %#v\n", sBooks)


	// equal to: [4]bool
	var published [len(books)]bool

	published[0] = true
	published[len(books)-1] = true

	fmt.Println("\nPublished Books:")
	for i, ok := range published {
		if ok {
			fmt.Printf("+ %s\n", books[i])
		}
	}
}

Array Literal

package main

func main() {

	var books [4]string
	books[0] = "kafka's Revenge"
	books[1] = "stay Golden"
	books[2] = "everythingship"
	books[3] = "kafka's revenge 2nd Editions"

}
func main() {

	var books = [4]string{
		"kafka's Revenge",
		"stay Golden",
		"everythingship",
		"kafka's revenge 2nd Editions",
	}
}
package main

func main() {

	books := [4]string{
		"kafka's Revenge",
		"stay Golden",
		"everythingship",
		"kafka's revenge 2nd Editions",
	}
}
package main

func main() {

	books := [...]string{
		"kafka's Revenge",
		"stay Golden",
		"everythingship",
		"kafka's revenge 2nd Editions",
	}
}

Comparing  Array 

6

9

3

6

9

3

[3]int{6,9,3}
[3]int{6,9,3}

they've identical types

package main

import "fmt"

func main() {

	var (
		blue = [3]int{6, 9, 3}
		red  = [3]int{6, 9, 3}
	)
	fmt.Printf("blue bookcase : %v\n", blue)
	fmt.Printf("red bookcase  : %v\n", red)

	fmt.Println("Are they equal?", blue == red)
}

multi-dimensional arrays ?

[3]int{9,8,4}
[3]int{5,6,1}

1st Student's Grades 

2nd Student's Grades 

5

6

1

9

8

4

5

6

1

9

8

4

5

6

1

9

8

4

5

6

1

9

8

4

[2][3] int {
  [3]int{5,6,1},
  [3]int{9,8,4},
}

outer array

[2][3] int

contains the inner arrays

1st inner array 

[3] int

2nd inner array 

[3] int

[2][3] int {
  [3]int{5,6,1},
  [3]int{9,8,4},
}

the length of the array

[2][3] int {
  [3]int{5,6,1},
  [3]int{9,8,4},
}

the element type of the array

[2][3] int {
  [3]int{5,6,1},
  [3]int{9,8,4},
}

element type of

the inner array

[2][3] int {
  {5,6,1},
  {9,8,4},
}

package main

import "fmt"

func main() {
	

	students := [...][3]float64{
		{5, 6, 1},
		{9, 8, 4},
	}

	var sum float64

	for _, grades := range students {
		for _, grade := range grades {
			sum += grade
		}
	}

	const N = float64(len(students) * len(students[0]))
	fmt.Printf("Avg Grade: %g\n", sum/N)

	
}

keyed elements

package main

import "fmt"

func main() {
	rates := [3]float64{
		0.5,
		2.5,
		1.5,
	}

	fmt.Println(rates)
}
package main

import "fmt"

func main() {
	rates := [3]float64{
		0: 0.5, // index: 0
		1: 2.5, // index: 1
		2: 1.5, // index: 2
	}

	fmt.Println(rates)
    
}
package main

import "fmt"

func main() {
	rates := [3]float64{
		1: 2.5, // index: 1
		0: 0.5, // index: 0
		2: 1.5, // index: 2
	}
	fmt.Println(rates)

}
package main

import "fmt"

func main() {
	rates := [3]float64{
		// index 0 empty
		// index 1 empty
		2: 1.5, // index: 2
	}

	fmt.Println(rates)
 }
    
package main

import "fmt"

func main() {
	rates := [...]float64{
		// index 0 empty
		// index 1 empty
		// index 2 empty
		// index 3 empty
		// index 4 empty
		5: 1.5, // index: 5
	}

	fmt.Println(rates)
}
package main

import "fmt"

func main() {
	rates := [...]float64{
		// index 1 to 4 empty

		5:   1.5, // index: 5
		2.5, //      index: 6
		0:   0.5, // index: 0
	}

	fmt.Println(rates)

}

Slice & Internals

  • slice vs array
  • Growing - Append()
  • Slicing - Slice Expressions 
  • Internals - backing array , slice header , capacity of a slice - cap()
  • Full Slice Expressions
  • Preallocation - make()
  • Coping slices without a loop - copy()
  • Multi-Dimensional Slices

Slices vs Arrays

0

0

0

Arrays cannot grow or shrink

0

0

0

Array 

you cannot add or remove elements to/from an array in runtime

var nums [2]int

its length is part of its type 

its length belong to compile-time

so its length cannot change in runtime 

slice

you can add or remove elements to/from a slice in runtime

var nums []int

its doesn't have a fixed length at compile-time

so it can grow and shrink in runtime

its length belongs to runtime

var nums [] int

nums is nil

package main

import (
	"fmt"
)

func main() {
	games := []string{"kokemon", "sims"}
	newGames := []string{"pacman", "doom", "pong"}

	newGames = games
	games = nil

	games = []string{}
	var ok string
	for i, game := range games {
		if game != newGames[i] {
			ok = "not "
			break
		}
	}

	// if games == nil {
	// 	ok = "not"
	// }

	if len(games) != len(newGames) {
		ok = "not"
	}

	fmt.Printf("games and newGames are %sequal \n\n", ok)
	fmt.Printf("games : %#v\n", games)
	fmt.Printf("new games :%#v\n", newGames)
}

can you compare a slice to another one ?

Append

append(slice,newElement)

appends a new elements to a slice

1

2

3

nums := []int{1,2,3}
append(nums,4)
nums := []int{1,2,3}
nums = append(nums,4)

1

2

3

12

13

nums := []int{1,2,3}
tens := []int{12,13}
nums = append(nums,tens...)
package main

import (
	s "github.com/inancgumus/prettyslice"
)

func main() {
	var todo []string

	todo = append(todo, "sing")

	todo = append(todo, "run")

	todo = append(todo, "code", "play")
	tomorrow := []string{"see mom", "learn go"}
	todo = append(todo, tomorrow...)
	s.Show("todo", todo)
}

slicing slice

newSlice := sliceable[START:STOP]

slicing returns the sliced parts

array or slice to be sliced 

slice it up to this 

"elements position"

slice it starting from this "index"

package main

import (
	"fmt"

	s "github.com/inancgumus/prettyslice"
)

func main() {

	items := []string{
		"pacman", "mario", "tetris", "doom",
		"galaga", "frogger", "asteroids", "simcity",
		"metroid", "defender", "rayman", "tempest",
		"ultima",
	}

	s.MaxPerLine = 4
	s.Show("All items", items)

	top3 := items[:3]
	s.Show("Top 3 items", top3)

	l := len(items)

	
	last4 := items[l-4:]
	s.Show("Last 4 items", last4)

	
	mid := last4[1:3]
	s.Show("Last4[1:3]", mid)


	fmt.Printf("slicing : %T %[1]q\n", items[2:3])
	fmt.Printf("indexing: %T %[1]q\n", items[2])
}

Backing Array

package main

import s "github.com/inancgumus/prettyslice"

func main() {
	// ages, first and last2 have the same backing arrays
	ages := []int{35, 15, 25}
	first := ages[0:1]
	last2 := ages[1:3]

	ages[0] = 55
	ages[1] = 10
	ages[2] = 20

	// grades and ages have separate backing arrays
	grades := []int{70, 99}
	grades[0] = 50

	s.Show("ages", ages)
	s.Show("ages[0:1]", first)
	s.Show("ages[1:3]", last2)
	s.Show("grades", grades)

	{
		// ages and agesArray have the same backing arrays
		agesArray := [3]int{35, 15, 25}
		ages := agesArray[0:3]

		ages[0] = 100
		ages[2] = 50

		s.Show("agesArray", agesArray[:])
		s.Show("agesArray's ages", ages)
	}
}

slice header 

package main

import "fmt"

func main() {
    numbers := [5]int{1, 2, 3, 4, 5}

    //Both start and end
    num1 := numbers[2:4]
    fmt.Println("Both start and end")
    fmt.Printf("num1=%v\n", num1)
    fmt.Printf("length=%d\n", len(num1))
    fmt.Printf("capacity=%d\n", cap(num1))


    num2 := numbers[2:]
    fmt.Println("\nOnly start")
    fmt.Printf("num1=%v\n", num2)
    fmt.Printf("length=%d\n", len(num2))
    fmt.Printf("capacity=%d\n", cap(num2))


    num3 := numbers[:3]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num3)
    fmt.Printf("length=%d\n", len(num3))
    fmt.Printf("capacity=%d\n", cap(num3))

    num4 := numbers[:]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num4)
    fmt.Printf("length=%d\n", len(num4))
    fmt.Printf("capacity=%d\n", cap(num4))
}

Make

make initalizes and returns a slice with the given Length and Capacity

make([]int , 3)

the type of 

the slice

the length &

the capacity of slice

make([]int , 3,5)

capacity of the slice

length of the slice

s:= make([]int , 0,5)
s = append(s,42)
ackage main

import "fmt"

func main() {
	a := make([]int, 5)
	printSlice("a", a)

	b := make([]int, 0, 5)
	printSlice("b", b)

	c := b[:2]
	printSlice("c", c)

	d := c[2:5]
	printSlice("d", d)
}

func printSlice(s string, x []int) {
	fmt.Printf("%s len=%d cap=%d %v\n",
		s, len(x), cap(x), x)
}

Copy

copies the elements

of a slice to another slice

copy(slice,slice2)

source 

slice

destination

slice


package main

import "fmt"

func main() {
	
	slc1 := []int{58, 69, 40, 45, 11, 56, 67, 21, 65}
	var slc2 []int
	slc3 := make([]int, 5)
	slc4 := []int{78, 50, 67, 77}


	fmt.Println("Slice_1:", slc1)
	fmt.Println("Slice_2:", slc2)
	fmt.Println("Slice_3:", slc3)
	fmt.Println("Slice_4:", slc4)

	
	copy_1 := copy(slc2, slc1)
	fmt.Println("\nSlice:", slc2)
	fmt.Println("Total number of elements copied:", copy_1)

	copy_2 := copy(slc3, slc1)
	fmt.Println("\nSlice:", slc3)
	fmt.Println("Total number of elements copied:", copy_2)

	copy_3 := copy(slc4, slc1)
	fmt.Println("\nSlice:", slc4)
	fmt.Println("Total number of elements copied:", copy_3)

	copy_4:= copy(slc1, slc4)
	fmt.Println("\nSlice:", slc1)
	fmt.Println("Total number of elements copied:", copy_4)
	
}

Multi-dimensions slice

package main

import "fmt"

func main() {
	sales := [][]int{
		{100, 200},
		{300},
		{400, 500},
	}

	for _, x := range sales {
		for _, y := range x {
			fmt.Println(y)
		}
	}
}

UTF -8 : Encode and Decode

  • bytes , runes & strings
  • unicode code points 
  • Character sets 
  • Printing and converting 
  • indexing and slicing strings 

basic of bytes , runes and strings

[]byte{104,101,121}

"hey"

[]byte("hey")
string[]byte{104,101,121}

'h'      'e'      'y'

104    101   121

Rune Literals 

Unicode Code Points

aka = Runes

package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	var start, stop int

	if args := os.Args[1:]; len(args) == 2 {
		start, _ = strconv.Atoi(args[0])
		stop, _ = strconv.Atoi(args[1])
	}

	if start == 0 || stop == 0 {
		start, stop = 'A', 'Z'
	}

	fmt.Printf("%-10s %-10s %-10s %-12s\n%s\n",
		"literal", "dec", "hex", "encoded",
		strings.Repeat("-", 45))

	for n := start; n <= stop; n++ {
		fmt.Printf("%-10c %-10[1]d %-10[1]x % -12x\n", n, string(n))
	}
}

character set 

Converting , indexing & strings

package main

import (
	"fmt"
	"unicode/utf8"
	"unsafe"
)

func main() {
	str := "Yūgen ☯ 💀"

	bytes := []byte(str)


	str = string(bytes)

	fmt.Printf("%s\n", str)
	fmt.Printf("\t%d bytes\n", len(str))
	fmt.Printf("\t%d runes\n", utf8.RuneCountInString(str))
	fmt.Printf("% x\n", bytes)
	fmt.Printf("\t%d bytes\n", len(bytes))
	fmt.Printf("\t%d runes\n", utf8.RuneCount(bytes))


	fmt.Println()
	fmt.Printf("1st byte   : %c\n", str[0])           // ok
	fmt.Printf("2nd byte   : %c\n", str[1])           // not ok
	fmt.Printf("2nd rune   : %s\n", str[1:3])         // ok
	fmt.Printf("last rune  : %s\n", str[len(str)-4:]) // ok

	
	runes := []rune(str)

	fmt.Println()
	fmt.Printf("%s\n", str)
	fmt.Printf("\t%d bytes\n", int(unsafe.Sizeof(runes[0]))*len(runes))
	fmt.Printf("\t%d runes\n", len(runes))

	fmt.Printf("1st rune   : %c\n", runes[0])
	fmt.Printf("2nd rune   : %c\n", runes[1])
	fmt.Printf("first five : %c\n", runes[:5])

	fmt.Println()

	word := "öykü"
	fmt.Printf("%q in runes: %c\n", word, []rune(word))
	fmt.Printf("%q in bytes: % [1]x\n", word)

	fmt.Printf("%s %s\n", word[:2], []byte{word[0], word[1]}) // ö
	fmt.Printf("%c\n", word[2])                               // y
	fmt.Printf("%c\n", word[3])                               // k
	fmt.Printf("%s %s\n", word[4:], []byte{word[4], word[5]}) // ü
}

Rune Decoding

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	const text = `Galaksinin Batı Sarmal Kolu'nun bir ucunda, haritası bile çıkarılmamış ücra bir köşede, gözlerden uzak, küçük ve sarı bir güneş vardır.
Bu güneşin yörüngesinde, kabaca yüz kırksekiz milyon kilometre uzağında, tamamıyla önemsiz ve mavi-yeşil renkli, küçük bir gezegen döner.
Gezegenin maymun soyundan gelen canlıları öyle ilkeldir ki dijital kol saatinin hâlâ çok etkileyici bir buluş olduğunu düşünürler.`

	r, size := utf8.DecodeRuneInString("öykü")
	fmt.Printf("rune: %c size: %d bytes.\n", r, size)

	r, size = utf8.DecodeRuneInString("ykü")
	fmt.Printf("rune: %c size: %d bytes.\n", r, size)

	r, size = utf8.DecodeRuneInString("kü")
	fmt.Printf("rune: %c size: %d bytes.\n", r, size)

	r, size = utf8.DecodeRuneInString("ü")
	fmt.Printf("rune: %c size: %d bytes.\n", r, size)

	// for range loop automatically decodes the runes
	//   but it gives you the position of the current rune
	//   instead of its size.

	// for _, r := range text {}
	for i := 0; i < len(text); {
		r, size := utf8.DecodeRuneInString(text[i:])
		fmt.Printf("%c", r)

		i += size
	}
	fmt.Println()
}
package main

import (
	"fmt"
	"unsafe"
)

func main() {
	// empty := ""
	// dump(empty)

	hello := "hello"
	dump(hello)
	dump("hello")
	dump("hello!")

	for i := range hello {
		dump(hello[i : i+1])
	}

	dump(string([]byte(hello)))
	dump(string([]byte(hello)))
	dump(string([]rune(hello)))
}

// StringHeader is used by a string value
// In practice, you should use: reflect.Header
type StringHeader struct {
	// points to a backing array's item
	pointer uintptr // where it starts
	length  int     // where it ends
}

// dump prints the string header of a string value
func dump(s string) {
	ptr := *(*StringHeader)(unsafe.Pointer(&s))
	fmt.Printf("%q: %+v\n", s, ptr)
}

string header

Maps and Internals

package main

import (
	"fmt"
)

func main() {
	
	var dict map[string]string
	key := "good"

	value := dict[key]
	fmt.Printf("%q means %#v\n", key, value)

	fmt.Printf("# of Keys: %d\n", len(dict))

}
package main

import "fmt"

func main() {

    m := make(map[string]int)

    m["k1"] = 7
    m["k2"] = 13

    fmt.Println("map:", m)

    v1 := m["k1"]
    fmt.Println("v1: ", v1)

    fmt.Println("len:", len(m))

    delete(m, "k2")
    fmt.Println("map:", m)

    _, prs := m["k2"]
    fmt.Println("prs:", prs)

    n := map[string]int{"foo": 1, "bar": 2}
    fmt.Println("map:", n)
}

Struct

structs cannot dynamically grow but they can have different set of types

slice & map  struct 
single element type Different types of fields
Dynamic Number of elements fixed number of field
package main

import "fmt"

func main() {
	type Movie struct {
		Title  string
		Genre  string
		Rating int
	}

	type Rental struct {
		Address string
		Rooms   int
		Size    int
		Price   int
	}

	type Person struct {
		Name     string
		Lastname string
		Age      int
	}

	person1 := Person{Name: "Pablo", Lastname: "Guzman", Age: 91}
	person2 := Person{Name: "sangam", Lastname: "biradar", Age: 83}

	fmt.Printf("person1: %+v\n", person1)
	fmt.Printf("person2: %+v\n", person2)

	type VideoGame struct {
		Title     string
		Genre     string
		Published bool
	}

	pacman := VideoGame{
		Title:     "Pac-Man",
		Genre:     "Arcade Game",
		Published: true,
	}

	fmt.Printf("pacman: %+v\n", pacman)
}

lets create struct 

compare struct

package main

import (
"fmt"
"reflect"
)

type testStruct struct {
	A int
	B string
	C []int
}

func main() {
	st1 := testStruct{A:100,B:"Australia",C:[]int{1,2,3}}	
	st2 := testStruct{A:100,B:"Australia",C:[]int{1,2,3}}
	fmt.Println("Struct equal: ", reflect.DeepEqual(st1, st2))
	
	
	slice1 := []int{1,2,3,4}
	slice2 := []int{1,2,3,4}
	fmt.Println("Slice equal: ", reflect.DeepEqual(slice1, slice2))
	
	map1 := map[string]int{	
		"x":1,
		"y":2,
	}
	map2 := map[string]int{	
		"x":1,
		"y":2,
		"z":3,
	}
	fmt.Println("Map equal: ",reflect.DeepEqual(map1, map2))
}
package main

import (
	"fmt"
)

func main() {
	// #1: declare the types
	type text struct {
		title string
		words int
	}

	type book struct {
		// title string
		// words int

		// #3: include the text as a field
		// text text

		// #4: embed the text
		text
		isbn string

		// #5: add a conflicting field
		title string
	}

	// #2: print a book
	// moby := book{title: "moby dick", words: 206052, isbn: "102030"}
	// fmt.Printf("%s has %d words (isbn: %s)\n", moby.title, moby.words, moby.isbn)

	// #3b: type the text in its own field
	moby := book{
		// #5c: type the field in a new field
		// title: "conflict",
		text: text{title: "moby dick", words: 206052},
		isbn: "102030",
	}

	moby.text.words = 1000
	moby.words++

	// // #4b: print the book
	fmt.Printf("%s has %d words (isbn: %s)\n",
		moby.title, // equals to: moby.text.title
		moby.words, // equals to: moby.text.words
		moby.isbn)

	// #3c: print the book
	// fmt.Printf("%s has %d words (isbn: %s)\n",
	// 	moby.text.title, moby.text.words, moby.isbn)

	// #5b: print the conflict
	fmt.Printf("%#v\n", moby)

	// go get -u github.com/davecgh/go-spew/spew
	// spew.Dump(moby)
}

Struct 

Embeddig

Json Encoding

JSON is a structured data exchange format

Mars

Message 

"hello"

`{ Message : "hello" }`

Earth

Message 

"hello"

Encodes 

Marshal

Decodes 

Unmarshals

package main

import (
	"encoding/json"
	"fmt"
)

type permissions map[string]bool // #3

type user struct { // #1
	Name        string      `json:"username"`
	Password    string      `json:"-"`
	Permissions permissions `json:"perms,omitempty"` // #6

	// name        string // #1
	// password    string // #1
	// permissions // #3
}

func main() {
	users := []user{ // #2
		{"inanc", "1234", nil},
		{"god", "42", permissions{"admin": true}},
		{"devil", "66", permissions{"write": true}},
	}

	// out, err := json.Marshal(users) // #4
	out, err := json.MarshalIndent(users, "", "\t") // #5
	if err != nil {                                 // #4
		fmt.Println(err)
		return
	}

	fmt.Println(string(out)) // #4
}
[
	{
		"username": "inanc"
	},
	{
		"username": "god",
		"perms": {
			"admin": true
		}
	},
	{
		"username": "devil",
		"perms": {
			"write": true
		}
	}
package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
)

type user struct {
	Name        string          `json:"username"`
	Permissions map[string]bool `json:"perms"`
}

func main() {
	var input []byte
	for in := bufio.NewScanner(os.Stdin); in.Scan(); {
		input = append(input, in.Bytes()...)
	}

	var users []user
	if err := json.Unmarshal(input, &users); err != nil {
		fmt.Println(err)
		return
	}

	for _, user := range users {
		fmt.Print("+ ", user.Name)

		switch p := user.Permissions; {
		case p == nil:
			fmt.Print(" has no power.")
		case p["admin"]:
			fmt.Print(" is an admin.")
		case p["write"]:
			fmt.Print(" can write.")
		}
		fmt.Println()
	}
}

Json Decoding

go run . > data.json

Chapter -3 : Functions   

 

Functions 

Reusable 

code

blocks

Functions

every function starts with a func keyword 

func name(inputParams){
// you write your code here ...
}
func sum(a int,b int) {}  =  func sum(a,b int){}
func print() {
// runs this code 
return 
// skips running the rest ....
}
func name(inputParams)(resultParams){
// you write your code here ...
}

type , type

func Atoi(s string) (int,error){}

Always declare an error result value as the last parameter

func Atoi(s string) (int,error){}
func Atoi(s string) (int,error){
// code to convery s to int

return 0,nil
}
func Atoi( i int) string{
// code to convert i to string

return "some string value"
}

you don't need to use a comma when 

returning a single value

Functions 

Input params and result values are copied 

n,m := 5,2 
res := multiply(n,m)
// res ----> 10 
// n   ----> 5 
// m   ----> 2 
func multiply(a,b int) int {

// a,b := 5,2 

a*= b 
return a 
}

multiply cannot change n or m 

it can only change a or b 

it copies and returns the local variable : a  

confine variables to a function

package main

import (
	"fmt"
)

func main() {
	local := 10
	show(local)

	incrWrong(local)
	fmt.Printf("main -> local = %d\n", local)
}

func show(n int) {
	fmt.Printf("show -> n = %d\n", n)
}

func incrWrong(n int) {

	// n:= main's local variable's value
	n++
}
package main

import (
	"fmt"
	"strconv"
)

func main() {
	local := 10
	show(local)

	incrWrong(local)
	fmt.Printf("local → %d\n", local)

	// incr(local)
	local = incr(local)
	show(local)

	local = incrBy(local, 5)
	show(local)

	_, err := incrByStr(local, "TWO")
	if err != nil {
		fmt.Printf("err   → %s\n", err)
	}

	local, _ = incrByStr(local, "2")
	show(local)

	// CHAINING

	// can't save the number back into main's local
	show(incrBy(local, 2))
	show(local)

	// can't pass the results of the multiple-value returning func
	// show(incrByStr(local, "3"))

	// can call showErr directly because it accepts the same types
	// of parameters as with incrByStr's result values.
	// local = sanitize(incrByStr(local, "NOPE"))
	// show(local)

	local = sanitize(incrByStr(local, "2"))
	show(local)

	local = limit(incrBy(local, 5), 2000)
	show(local)
}

func show(n int) {
	// can't access main's local
	// fmt.Printf("show : n = %d\n", local)
	fmt.Printf("show  → n = %d\n", n)
}

// wrong: incr can't access to main's `local`
func incrWrong(n int) {
	// n := local
	n++
	// can't return (there are no result values)
	// return n
}

func incr(n int) int {
	n++
	return n
}

func incrBy(n, factor int) int {
	return n * factor
}

func incrByStr(n int, factor string) (int, error) {
	m, err := strconv.Atoi(factor)
	n = incrBy(n, m)
	return n, err
}

func sanitize(n int, err error) int {
	if err != nil {
		return 0
	}
	return n
}

func limit(n, lim int) (m int) {
	// var m int
	m = n
	if m >= lim {
		m = lim
	}
	// return m
	return
}

Variadic parameters 

package main

import "fmt"

func average(sf ...float64) float64 {
	total := 0.0
	for _, v := range sf {
		total += v
	}
	return total / float64(len(sf))
}

func main() {
	n := average(43, 56, 87, 12, 45, 57)
	fmt.Println(n)
}
package main

import "fmt"

func average(sf ...float64) float64 {
	total := 0.0
	for _, v := range sf {
		total += v
	}
	return total / float64(len(sf))
}

func main() {
	data := []float64{43, 56, 87, 12, 45, 57}
	n := average(data...)
	fmt.Println(n)
}

write a function with one variadic parameter that finds the greater number in list of numbers

package main

import "fmt"

func max(numbers ...int) int {
	var largest int
	for _, v := range numbers {
		if v > largest {
			largest = v
		}
	}
	return largest
}

func main() {
	greatest := max(4, 7, 9, 123, 543, 23, 435, 53, 125)
	fmt.Println(greatest)
}
package main

import "fmt"

func max(numbers ...int) int {
	var largest int
	for _, v := range numbers {
		if v > largest {
			largest = v
		}
	}
	return largest
}

func main() {
        fmt.Println(max) //max is the function 
	max := max(4, 7, 9, 123, 543, 23, 435, 53, 125)
	fmt.Println(max)
}

// don't do this ; bad coding pratice to showdow variables 

Func Expressions 

setting a variable equal to a function

package main 

import "fmt"

func greeting() {

fmt.Println("hello world")
}

func main() {

greeting()
}
package main

import "fmt"

func main() {

	greeting := func() {

		fmt.Println("hello world")
	}
	greeting()
}
package main

import "fmt"

func main() {

	greeting := func() {

		fmt.Println("hello world")
	}
	greeting()
    fmt.Printf("%T\n" , greeting)
}

another func expression 

setting a variable equal to a func 

package main 

import "fmt"

func main() {

half := func(n int ) ( int , bool) {
     return n/2 , n%2 == 0 
}
  fmt.Println(half(2))
}
package main 

import "fmt"

func main() {

add := func(x , y int ) int  {
     return x + y 
}
  fmt.Println(add(1,4))
}

Closure

"one thing enclosing another thing"

package main 
import "fmt"

func main() {

x := 0 
 increment := func() int {
    x++ 
    return x 
 }
 
 fmt.Println(increment())
 fmt.Println(increment())
}
// scope of x is func main 
// func incremet has access to x 

Closure 

func  main encloses func increment 

 

closure helps us limit the scope of variables that are used by multiple functions

without closure, for two or more funcs to have access to the same variable, that variable would need to be package scope

func main is enclosing increment; increment is enclosing x

 

package main 
import "fmt"

var x = 0

func increment() int {

x++ 
return x 

}

func main() {

fmt.Println(increment())
fmt.Printlb(increment())
}

Not using Closure

closure helps us limit the scope of variable 

that used by multiple functions 

without closure for two or more funcs to 

have access  to the same variable , that variable would need to be a package scope 

Returning a func 

package main

import "fmt"

func makeEvenGenerator() func() int {

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

func main() {

	nextEven := makeEvenGenerator()
	fmt.Println(nextEven()) //2
	fmt.Println(nextEven()) //4
	fmt.Println(nextEven()) //6

	masEven := makeEvenGenerator()
	fmt.Println(masEven()) //2
	fmt.Println(masEven()) //4
	fmt.Println(masEven()) //6
}

callback 

passing a func as an argument 

package main

import "fmt"

func visit(numbers []int, callback func(int)) {

	for _, n := range numbers {

		callback(n)
	}
}

func main() {

	visit([]int{1, 2, 3, 4}, func(n int) {

		fmt.Println(n)
	})
}
package main

import "fmt"

func visit(numbers []int, callback func(int)) {

	for _, n := range numbers {

		callback(n)
	}
}

func main() {

	visit([]int{1, 2, 3, 4}, func(n int) {

		fmt.Println(n)
	})
}

func visit takes two arguments

a slice of ints 

another func 

the callback

package main

import "fmt"

func visit(numbers []int, callback func(int)) {

	for _, n := range numbers {

		callback(n)
	}
}

func main() {

	visit([]int{1, 2, 3, 4}, func(n int) {

		fmt.Println(n)
	})
}

func visit takes two arguments

a slice of ints 

another func 

the callback

pass in the slice of ints

package main

import "fmt"

func visit(numbers []int, callback func(int)) {

	for _, n := range numbers {

		callback(n)
	}
}

func main() {

	visit([]int{1, 2, 3, 4}, func(n int) {

		fmt.Println(n)
	})
}

the func passed as an argument

(the callback) is assigned to the parameter "callback" and then get used

Recursion

a func that can call itself 

  • factorial(4)

    • returns: 4 * factorial(3)

  • factorial(3)

    • returns: 3 * factorial(2)

  • factorial(2)

    • returns: 2 * factorial(1)

  • factorial(1)

    • returns: 1 * factorial(0)

  • factorial(0)

    • returns: 1

----------------------------------------------------

returns: 4 * 3 * 2 * 1 * 1

The End Result:

  • 4 * 3 * 2 * 1

package main 
import "fmt"

func factorial(x unit) uint {

if x == 0 {

   return 1 
}

return x * factorial(x-1)
}

func main(){
fmt.Println(factorial(4))
}

Defer 

run this at the last  possible moment

pacakge main 
import "fmt" 

func hello(){
  fmt.Print("hello")
}

func world() {

fmt.Println('world')
}

func main() {

defer world()
hello()
}

Chapter - 4 : Concurrency &

Parallelism    

 

" concurrency is the composition of independent executing processes , while parallelism is simultaneous execution of (possibly related) computation . Concurrency is about dealing with lots of things at once . Parallelism is about lot of things at once " 

a

c

b

Concurrency 

doing many things but only at a time 

" multi-tasking"

a

c

b

Parallelism

doing many things at the same time 

https://www.cloudbees.com/blog/visualizing-concurrency-go

//create a channel of type int
//make(chan <data type>)
c := make(chan int)
//attach or send a value into channel
c <- 43
//retrieve a value from channel
<-c
func main() {
    //create a channel
    c := make(chan int)

    //put a value into channel
    c <- 43

    //retrieve a value from channel
    fmt.Println("value of channel c:", <-c)
}
func main() {
    //create a channel
    c := make(chan <-int)

    //put a value into channel
    //wrapped inside goroutine
    go func() {
        c <- 43
    }()

    //retrieve a value from channel
    fmt.Println("value of channel c:", <-c)
}
//create a channel that only available for reading value 
cread := make(<-chan int)
//create a channel that only available for sending value
csend := make(chan<- int)
//create a channel that available for 25 data that has a type of int
c := make(chan int, 25)

buffer channel

select and close()

When creating a channel, the close() function can be called to close a channel when sending operation to a channel is finished. Here it is the example.

func main() {
    //create a channel with capacity of 10 ints
    c := make(chan int, 10)

    //put a value into channel
    //wrapped inside goroutine
    go func() {
        //send value from 1-10 into channel
        for i := 1; i <= 10; i++ {
            c <- i
        }
        //close the channel
        //this means that the sending operation is finished
        close(c)
    }()

    //retrieve a value from channel using for loop
    for v := range c {
        fmt.Println("Value from c:", v)
    }

    fmt.Println("Bye..")
}
Value from c: 1
Value from c: 2
Value from c: 3
Value from c: 4
Value from c: 5
Value from c: 6
Value from c: 7
Value from c: 8
Value from c: 9
Value from c: 10
Bye..

When receiving some values from channel with certain condition or from certain channel, the select {..} can be used to determine which channel's value needs to be received. Here it is the example of using select{..}.

func main() {
    //create a channels
    frontend := make(chan string)
    backend := make(chan string)
    quit := make(chan string)

    //send some values to channels with goroutine
    go send(frontend, backend, quit)

    //receive some values from channels
    receive(frontend, backend, quit)
}

func send(f, b, q chan<- string) {
    data := []string{"React", "NodeJS", "Vue", "Flask", "Angular", "Laravel"}
    for i := 0; i < len(data); i++ {
        if i%2 == 0 {
            //send value to channel f
            f <- data[i]
        } else {
            //send value to channel b
            b <- data[i]
        }
    }
    //send value to channel q
    q <- "finished"
}

func receive(f, b, q <-chan string) {
    for {
        //using select to choose certain channel
        // that the value need to be received
        select {
        //if the value comes from channel called "f"
        //then execute the code
        case v := <-f:
            fmt.Println("Front End Dev:", v)
        //if the value comes from channel called "b"
        //then execute the code
        case v := <-b:
            fmt.Println("Back End Dev:", v)
        //if the value comes from channel called "q"
        //then execute the code
        case v := <-q:
            fmt.Println("This program is", v)
            return //finish the execution
        }
    }
}
// Go program to illustrate
// the concept of Goroutine
package main

import "fmt"

func display(str string) {
	for w := 0; w < 6; w++ {
		fmt.Println(str)
	}
}

func main() {

	// Calling Goroutine
	go display("Welcome")

	// Calling normal function
	display("Golang workshop")
}