Skip to main content

Golang Basics

Variables and functions

package main

import "fmt"

// Global variable declaration and they are set to default initializations. And the scope is with in the main package
var (
gbNum int // default 0
gbFlag bool // default false
gbString string // default empty string and its not "null"
)

func main() {

// Local variables: scope is with in main function
var localStrSays string
var i int

localStrSays = "I'm local Var"
fmt.Println(localStrSays)

i = 8
fmt.Println("i is set to", i)

firstReturnParaFunc, secondReturnParaFunc, thirdReturnParaFunc := saySomeThing()

fmt.Println("The function returned:\"", firstReturnParaFunc, "\", And then second is: ", secondReturnParaFunc, "Third para: ", thirdReturnParaFunc)

fmt.Println("")
fmt.Println("*** Default values set for global variables ***")
fmt.Println("The gbNum", gbNum)
fmt.Println("The gbFlag", gbFlag)
fmt.Println("The gbString", gbString)
}

//simple function returning three parameters (string, bool, int)
func saySomeThing() (string, bool, int) {
return "I am Para One", true, 456
}

Pointers

package main

import "fmt"

func main() {
var myStrColor string

myStrColor = "Yellow"

fmt.Println("myStrColor value is set to:", myStrColor)
updateColorWithPointer(&myStrColor) // & ampersand for reference (address of myStrColor)
fmt.Println("The updated color is: ", myStrColor)
updateColorWithPointer2(&myStrColor)
fmt.Println("The updated color is: ", myStrColor)

}

func updateColorWithPointer(colorChange *string) {
fmt.Println("String received is:", colorChange)

//creating a new local variable with a new string
newColorValue := "RED"

// * asterisk for pointer (value at address)
*colorChange = newColorValue //The value of newColorValue variable is stored in the memory of colorChange variable
}

func updateColorWithPointer2(colorChange *string) {
fmt.Println("String received is:", colorChange)

*colorChange = "Hello"
}

Value VS Reference types

golang types

type struct

package main

import (
"fmt"
"time"
)

// struct starting with capital letter means the struct is visible
// outside the package and same applies for the fields as well
type Person struct {
FirstName string
LastName string
PhoneNumber string
Age int
BirthDate time.Time
}

func main() {
user := Person{
FirstName: "Sam",
LastName: "Jack",
}

fmt.Println(user)
}

Struct with functions

package main

import "fmt"

type myAddress struct {
myStreet string
}

// (rc *myAddress) is the receiver and best practice is to use pointers
func (rc *myAddress) printMyStreet() string {
return rc.myStreet
}

func main() {

myAdd := myAddress{
myStreet: "123 Abcd st",
}

fmt.Println("myAdd street value:", myAdd.myStreet)

// Accessing struct field through struct receiver func
fmt.Println("Getting value with receiver", myAdd.printMyStreet())
}

Maps and Slices

Maps

Map doesn't store the objects in an order.

package main

import "fmt"

type Person struct {
FirstName string
LastName string
Age int
}

func main() {

user := make(map[string]Person)

user["Sam"] = Person{
FirstName: "Sam",
LastName: "Don",
Age: 100,
}

user["Jack"] = Person{
FirstName: "Jack",
LastName: "Black",
Age: 44,
}

fmt.Println(user["Sam"])
fmt.Println(user["Jack"].FirstName, user["Jack"].LastName)
fmt.Println(len(user))
}
Output
{Sam Don 100}
Jack Black
2

Slices

package main

import (
"fmt"
"sort"
)

func main() {
var mySlice []string

mySlice = append(mySlice, "Jack")
mySlice = append(mySlice, "Dog")
mySlice = append(mySlice, "Snake")

fmt.Println(mySlice)

var myIntSlice []int
myIntSlice = append(myIntSlice, 800)
myIntSlice = append(myIntSlice, 20)
myIntSlice = append(myIntSlice, 100)
fmt.Println(myIntSlice)

sort.Ints(myIntSlice)
fmt.Println("After Sorted")
fmt.Println(myIntSlice)

numSlice := []int{10, 20, 30, 40, 22, 33, 44, 777, 888, 999}

fmt.Println(numSlice)
fmt.Println("print from index [4:7]", numSlice[4:7])
fmt.Println("print from index [5:] ", numSlice[5:])

newSlice := numSlice[5:9]
fmt.Println("size of newSlice is: ", len(newSlice), " and has values: ", newSlice)
}
Output
[Jack Dog Snake]
[800 20 100]
After Sorted
[20 100 800]
[10 20 30 40 22 33 44 777 888 999]
print from index [4:7] [22 33 44]
print from index [5:] [33 44 777 888 999]
size of newSlice is: 4 and has values: [33 44 777 888]

Conditionals

if else

package main

import "fmt"

func main() {
checkAnimal := "Catsdf"
// isTrue is not necessary in the code,
// it is implemented to show multiple conditions can be implemented
isTrue := false

if checkAnimal == "Dog" {
fmt.Println("This is Dog")
} else if checkAnimal == "Cat" && !isTrue {
fmt.Println("This is Cat")
} else if checkAnimal == "Rino" && !isTrue {
fmt.Println("This is Rino")
} else if !isTrue {
fmt.Println("Nothing was found")
}
}
Output
Nothing was found

switch

package main

import (
"fmt"
)

func main() {
myAnimal := "cat"

// Once the match is found the associated case statements are executed and breaks out
switch myAnimal {
case "dog":
fmt.Println("This is Dog")
case "cat":
fmt.Println("This is cat")
case "rino":
fmt.Println("This is Rino")
default:
fmt.Println("Nothing is found")
}
}

loops

for - map slice

package main

import (
"fmt"
"strings"
)

func main() {
for i := 0; i < 3; i++ {
fmt.Println(i)
}

myAnimals := []string{"Cat", "Dog", "Rino"}
fmt.Println("### Loop through slice")
for _, v := range myAnimals {
fmt.Println(strings.ToUpper(v))
}

myThings := make(map[string]string)
myThings["computer"] = "Good"
myThings["pen"] = "Okay"
myThings["phone"] = "Very good"
fmt.Println("### Loop through maps")
for things, v := range myThings {
fmt.Println(things, ":", v)
}

}
Output
0
1
2
### Loop through slice
CAT
DOG
RINO
### Loop through maps
computer : Good
pen : Okay
phone : Very good

for - map struct

package main

import (
"fmt"
)

type Person struct {
FirstName string
LastName string
Age int
}

func main() {

users := make(map[string]Person)
users["101A"] = Person{
FirstName: "Jack",
LastName: "Simon",
Age: 10,
}

users["001A"] = Person{
FirstName: "AAA",
LastName: "Don",
Age: 100,
}

for id, user := range users {
fmt.Printf("ID: %s, First name: %s, Last name: %s, Age: %d\n",
id,
user.FirstName,
user.LastName,
user.Age,
)
}

}
Output
ID: 001A, First name: AAA, Last name: Don, Age: 100
ID: 101A, First name: Jack, Last name: Simon, Age: 10

Interfaces

package main

import "fmt"

type Vehicle interface {
NumberOfWheels() int
}

type Car struct {
Name string
EngineCC int
Color string
Doors int
}

// Implementing interface for Car
func (c *Car) NumberOfWheels() int {
return 2
}

type Truck struct {
Name string
EngineCC int
Color string
}

func (c *Truck) NumberOfWheels() int {
return 18
}

func main() {
myCar := Car{
Name: "Tesla",
Color: "Black",
EngineCC: 1000,
Doors: 2,
}

// Even though VehicleInfo function accepts only Vehicle type variable
// since the Car implements VehicleInfo interface it will work
VehicleInfo(&myCar)

myTruck := Truck{
Name: "Mercedes",
Color: "Red",
EngineCC: 2000,
}
VehicleInfo(&myTruck)

}

// This function receives an interface type Vehicle
func VehicleInfo(a Vehicle) {
fmt.Printf("This vehicle has : %d \n", a.NumberOfWheels())
}
Output
This vehicle has : 2
This vehicle has : 18

Channels

Generate a random value between 0 and 1000. The communication between main() and GetRandValue() is happening through channels.

package main

import (
"fmt"
"math/rand"
"time"
)

const numPool = 1000

// Generate a random value between 0 and n
func GenerateRandom(n int) int {
rand.Seed(time.Now().UnixNano())
newNum := rand.Intn(n)
return newNum
}

func GetRandValue(intChanC chan int) {
randomNum := GenerateRandom(numPool)
intChanC <- randomNum
}

func main() {
intChan := make(chan int)
defer close(intChan)

go GetRandValue(intChan)

getNum := <-intChan
fmt.Printf("Generated a random value between 0:%d is %d \n", numPool, getNum)
}
Output
Generated a random value between 0:1000 is 677

Test you code

main.go
package main

import (
"errors"
"fmt"
)

func main() {
result, err := getDivide(10, 9)

if err != nil {
fmt.Println(err)
return
}
fmt.Printf("The value of r is : %f \n", result)
}

func getDivide(a, b float32) (float32, error) {

if b == 0 {
return 0, errors.New("cannot divide by 0")
}
r := a / b

return r, nil
}

The code below is to test the getDivide() function with testing methods

test_main.go
package main

import "testing"

var tests = []struct {
name string
dividend float32
divisor float32
expected float32
isErr bool
}{
{"valid-data", 100.0, 10.0, 10.0, false},
{"valid-data", 100.0, 0.0, 0.0, true},
{"expect-5", 100.0, 20.0, 5.0, false},
}

func TestGetDivide(t *testing.T) {
for _, tt := range tests {
got, err := getDivide(tt.dividend, tt.divisor)
// Identifing the Error as per isErr value if the error occurs
if tt.isErr {
if err == nil {
t.Error("expected an error but did not get one")
}
} else {
if err != nil {
t.Error("did not expect an error but got one", err.Error())
}
}

// Checking if out from the function matches the expected output
if got != tt.expected {
t.Errorf("expected %f but got %f", tt.expected, got)
}
}
}
note

Create .mod file and execute the commands
command to execute test

To execute test with verbose output
go test -v
To execute test and create output file with coverage profile and open a html file format
go test -coverprofile=coverage.out && go tool cover -html=coverage.out