Basics

Go basics

In this section some basics of go are noted down.

Basic data types

Go's basic data types are,

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

Ref: A Tour of Go

Variables

Variable is the name given to a memory location to store a value of a specific type.

Declaring single variable

Syntax: var name type

Example:

var age int

Declaring variable with initial value

Syntax: var name type = initialvalue

Example:

var age int = 29

Type inference

If a variable has an initial value, Go will automatically be able to infer the type of that variable using that initial value.

Syntax: var name = initialvalue

Example:

var age = 29

Multiple variable declaration

Syntax: var name1, name2 type

Example:

var width, height int

With initial value

var width, height = 100, 50

Variables of different types

 var (
        name   string
        age    int
        height float32
    )

with initial value

 var (
        name   = "naveen"
        age    = 29
        height = 5.32
    )

Short hand declaration

Syntax: name := initialvalue

Example:

name := "emruz"
age, weight := 24, 65

Short hand syntax can only be used when at least one of the variables in the left side of := is newly declared.

a, b := 20, 30 // declare variables a and b
b, c := 40, 50 // b is already declared but c is new
a, b := 40, 50 //error, no new variables

Ref: GOLANGBOT.COM

Constant

Once declared, can not be reassigned,

Syntax: const name = value

Example:

const a = 55 //allowed
a = 89 //reassignment not allowed

The value of a constant should be known at compile time.

var a = math.Sqrt(4)//allowed
const b = math.Sqrt(4)//not allowed

b is a constant and the value of b needs to be know at compile time. The function math.Sqrt(4) will be evaluated only during run time and hence const b = math.Sqrt(4) throws error.

Go is strongly typed language. So, mixing type during assignment is not allowed.

Hence,

var defaultName = "Sam" //allowed. type of defaultName is "string"
type myString string
var customName myString = "Sam" //allowed. type of customName is "myString"
customName = defaultName //not allowed. Error: cannot use defaultName (type string) as type myString in assignment

Although myString is alias of string, Go does not allow to assign string type variable into myString type variable.

Constant does not have a type. They can provide a type on the fly depending on the context.

For example,

package main
import "fmt"

func main() {
    const name = "Sam"
    const age = 24
    fmt.Printf("value: %v type: %T\n", name, name)
    fmt.Printf("value: %v type: %T\n", age, age)
}

Output:

value: Sam type: string
value: 24 type: int

Again,

package main
import "fmt"

func main() {  
    const a = 5
    var intVar int = a
    var int32Var int32 = a
    var float64Var float64 = a
    var complex64Var complex64 = a
    fmt.Println("intVar",intVar, "\nint32Var", int32Var, "\nfloat64Var", float64Var, "\ncomplex64Var",complex64Var)
}

Output:

intVar 5
int32Var 5
float64Var 5
complex64Var (5+0i)

This program does not give any error because a is constant, so it does not have any type. When a is assigned to different type variable, it is taking type of this variable.

Ref: GOLANGBOT.COM

Functions

A function is a block of code that performs a specific task.

Structure:

func functionname(parametername type) returntype {  
 //function body
}

Sample function:

func calculateBill(price int, no int) int {  
    var totalPrice = price * no
    return totalPrice
}

If consecutive parameters has same type, writing type each time can be avoided by writing once at the end.

func calculateBill(price, no int) int {  
    var totalPrice = price * no
    return totalPrice
}

Multiple return value:

func rectProps(length, width float64)(float64, float64) {  
    var area = length * width
    var perimeter = (length + width) * 2
    return area, perimeter
}

Named returned value:

If a return value is named, it will be considered as a variable declared at the first line of the function. They don't need to return explicitly.

func rectProps(length, width float64)(area, perimeter float64) {  
    area = length * width
    perimeter = (length + width) * 2
    return //no explicit return value
}

Blank identifier: _ is know as the blank identifier in Go. It can be used in place of any value of any type. It is used to discard a returned value.

func rectProps(length, width float64) (float64, float64) {  
    var area = length * width
    var perimeter = (length + width) * 2
    return area, perimeter
}
func main() {  
    area, _ := rectProps(10.8, 5.6) // perimeter is discarded
    fmt.Printf("Area %f ", area)
}

Packages

Packages are used to organise go source code for better reusability and readability.

main function and main package

Every executable go application must contain a main function. This function is the entry point for execution. The main function should reside in the main package.

First line of every go source file should be package packagename which indicate that this source file belong to which pakage.

Source files belonging to a package should be placed in separate folders of their own. It is a convention in Go to name this folder with the same name of the package.

Package Example:

Sample Go project structure:

src
 |-- geometry
        |-- geometry.go
        |-- rectangle
                |-- rectprops.go

rectangle package:

//rectprops.go
package rectangle

import "math"

func Area(len, wid float64) float64 {  
    area := len * wid
    return area
}

func Diagonal(len, wid float64) float64 {  
    diagonal := math.Sqrt((len * len) + (wid * wid))
    return diagonal
}

main package:

//geometry.go
package main 

import (  
    "fmt"
    "geometry/rectangle" //importing custom package
)

func main() {  
    var rectLen, rectWidth float64 = 6, 7
    fmt.Println("Geometrical shape properties")
        /*Area function of rectangle package used
        */
    fmt.Printf("area of rectangle %.2f\n", rectangle.Area(rectLen, rectWidth))
        /*Diagonal function of rectangle package used
        */
    fmt.Printf("diagonal of the rectangle %.2f ",rectangle.Diagonal(rectLen, rectWidth))
}

Exported Names

Any variable or function which starts with a capital letter are exported names in go. Only exported functions and variables can be accessed from other packages.

init function

Every package can have init function.

The init function should not have any return type and should not have any parameters.

The init function cannot be called explicitly in our source code.

init function structure:

func init() {  
}

The init function can be used to perform initialisation tasks and can also be used to verify the correctness of the program before the execution starts.

The order of initialisation of a package:

  1. If a package imports other packages, the imported packages are initialised first.

  2. Package level variables are initialised then.

  3. init function is called next. A package can have multiple init functions (either in a single file or distributed across multiple files) and they are called in the order in which they are presented to the compiler.

A package will be initialised only once even if it is imported from multiple packages.

Initialisation Example:

rectangle package:

//rectprops.go
package rectangle

import "math"  
import "fmt"

/*
 * init function added
 */
func init() {  
    fmt.Println("rectangle package initialized")
}
func Area(len, wid float64) float64 {  
    area := len * wid
    return area
}

func Diagonal(len, wid float64) float64 {  
    diagonal := math.Sqrt((len * len) + (wid * wid))
    return diagonal
}

main package:

//geometry.go
package main 

import (  
    "fmt"
    "geometry/rectangle" //importing custom package
    "log"
)
/*
 * 1. package variables
*/
var rectLen, rectWidth float64 = 6, 7 

/*
*2. init function to check if length and width are greater than zero
*/
func init() {  
    println("main package initialized")
    if rectLen < 0 {
        log.Fatal("length is less than zero")
    }
    if rectWidth < 0 {
        log.Fatal("width is less than zero")
    }
}

func main() {  
    fmt.Println("Geometrical shape properties")
    fmt.Printf("area of rectangle %.2f\n", rectangle.Area(rectLen, rectWidth))
    fmt.Printf("diagonal of the rectangle %.2f ",rectangle.Diagonal(rectLen, rectWidth))
}

Output:

rectangle package initialized  
main package initialized  
Geometrical shape properties  
area of rectangle 42.00  
diagonal of the rectangle 9.22  

The order of initialisation of main package,

  1. Imported package are initialized first. Hence, rectangle package will be initialized first.

  2. Package level variable rectLen and rectWidth are initialized then.

  3. Then, init function of main package is called.

  4. Finally, main function is called.

Use of blank identifier

Go does not allow to import any package without using it. So,

  • If we want to import a package without using now but we may use later.

  • Or, we want to make sure that initialization of a particular pakage happen even though we don't want to use it.

We can use blank identifier ( _ ).

package main
import (
     _ "geometry/rectangle"
)
func main() {
}

Conditional statement (if else)

Simple if structure:

if condition {
    //statement
}

if-else statement:

if condition {
    //statement
} else {
    //statement
}

if - else if - else chain:

if condition {  
    //statement
} else if condition {
    //statement
} else {
    //statement
}

if variant:

if statement; condition {  
    //statement
}

Loop

Go has only for loop.

for loop structure:

for initialisation; condition; post { 
    //statement
}

Example:

for i := 1; i <= 10; i++ {
    fmt.Printf(" %d",i)
}

break

for i := 1; i <= 10; i++ {
    if i > 5 {
        break //loop is terminated if i > 5
    }
    fmt.Printf("%d ", i)
}

continue

for i := 1; i <= 10; i++ {
    if i%2 == 0 {
        continue
    }
    fmt.Printf("%d ", i)
}

Variant:

for ;i <= 10; { // initialisation and post are omitted
    fmt.Printf("%d ", i)
    i += 2
}
//multiple initialisation and increment
for no, i := 10, 1; i <= 10 && no <= 19; i, no = i+1, no+1 {
    fmt.Printf("%d * %d = %d\n", no, i, no*i)
}

Infinite loop:

for {  
    // statement
}

Switch Statement

Structure:

switch value {
case 1:
    // statement
case 2:
    // statement
case 3:
    // statement
default:
    // statement
}

Multiple Expression:

switch letter {
    case "a", "e", "i", "o", "u": //multiple expressions in case
        fmt.Println("vowel")
    default:
        fmt.Println("not a vowel")
    }

Expressionless switch:

switch is considered to be switch true and it will behave like if - else if chain.

switch { // expression is omitted
    case num >= 0 && num <= 50:
        fmt.Println("num is greater than 0 and less than 50")
    case num >= 51 && num <= 100:
        fmt.Println("num is greater than 51 and less than 100")
    case num >= 101:
        fmt.Println("num is greater than 100")
    }

Fallthrough:

When a case is executed, program control get out of the switch. A fallthrough statement is used to transfer control to the first statement of the case that is present immediately after the case which has been executed.

switch num := number(); { //num is not a constant
    case num < 50:
        fmt.Printf("%d is lesser than 50\n", num)
        fallthrough
    case num < 100:
        fmt.Printf("%d is lesser than 100\n", num)
        fallthrough
    case num < 200:
        fmt.Printf("%d is lesser than 200", num)
    }

Switch and case expressions need not be only constants. They can be evaluated at runtime too. In the program above num is initialised to the return value of the function number().

If the number is 75 then output will be.

75 is lesser than 100  
75 is lesser than 200

fallthrough should be the last statement in a case. If it present somewhere in the middle, the compiler will throw error fallthrough statement out of place

Last updated