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
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