© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
R. Hussain, M. ZulfiqarBeginning Go Programminghttps://doi.org/10.1007/978-1-4842-8858-0_2

2. Go Basics

Rumeel Hussain1   and Maryam Zulfiqar2
(1)
Dubai, United Arab Emirates
(2)
Lahore, Pakistan
 

What makes Go an easy-to-learn language is that everything you need to know about a Go program is right on the surface. There is no need to remember any language rules, as they are all in the application code. This chapter covers the basics of the Go programming language, like accepting input, using math operators and packages, managing memory, using different data structures, and understanding the program flow of a Go-based application. It also covers using functions and returning multiple values, as well as reading and writing data to files and handling JSON data.

Ancestors of Go

Go is based on a number of different languages. It was originally designed to be the next generation of the C language. It’s capable of performing everything you can do with C, including system programming, application development, and so on.

Go Syntax

Go borrows a lot of its syntax from C and its related languages—C++, C#, Java, and so on. Along with this, it also borrows syntax from Pascal, Modula, Oberon, and other similar languages. One of the biggest attractions of Go is that its programs are very concise because of the precise and comprehensive set of keywords and syntax. In order to be effective with any language, you have to know the certain basic syntax rules of that particular language. The following is a list of some of the most important syntax rules set by the designers of the Go programming language.
  1. 1.
    Case sensitivity
    • You must spell identifiers like functions, variables, and type names in the exact manner mentioned in the official documentation (https://go.dev/doc/).

    • Names for packages and variables use lowercase and mixed case.

    • Methods and fields of a type are initial capped.

    • In Go, initial uppercase characters indicate a special purpose.
      • Equivalent to the public keyword in other high-level programming languages like C++, C#, Java, and so on, in Go, an initial uppercase character indicates that the symbol is exported.

      • Similarly, equivalent to the private keyword, in Go, a lowercase initial character indicates that the particular method or variable is not exported nor accessible by the rest of the application other than the code block it is declared in.

     
  2. 2.
    Semicolons can be skipped
    • For the sake of reducing the amount of typing a developer has to do, as shown in Listing 2-1, Go eliminates semicolons from the ends of lines. This means that line feeds end the statement and no semicolons are required.

      var color [2] string
      colors[0] = "black"
      colors[1] = "white"
      Listing 2-1

      Semicolons Are Not Required as End-of-Line Indicators

    Even though the language specification requires semicolons, it is not necessary to type them explicitly. Thanks to Lexer, the software component that parses the code and analyses it, semicolons are added during the compilation process as needed.

    The Rule Followed by Lexer for Adding Semicolons

    When a statement is determined to be complete and Lexer encounters a line feed, meaning it’s the end of the statement and the developer has not explicitly added a semicolon, Lexer will do so.

    Note

    You can’t always add line feeds freely or break up statements with extra line feeds, as you can in other languages, because in certain cases they can be misinterpreted by Lexer.

     
  3. 3.
    Code blocks with braces
    • Code blocks, i.e., multiple lines of code that are intended to be grouped together, are wrapped within braces. See Listing 2-2.

      sum := 0
      for i :=0; i<10; i++{
        sum += i
      }
      fmt.Println(sum) //prints '45'
      Listing 2-2

      Braces Are Used to Indicate Code Blocks

    In Listing 2-2, the variable named sum is declared and then assigned a value of zero. The for loop is initialized from 0 to 10 and is used to increment the value of the sum by i. After the loop terminates, the value stored in the sum variable is printed using a function called Println or “print line”. Println is a built-in function of the package called FMT. The code that you want to iterate over is wrapped between two braces. Note that it is important to make sure that the opening brace is on the same line as any preceding statement.

     
  4. 4.
    Built-in functions
    • Go supports a set of several built-in functions that are always available in your code without having to import anything. These functions are members of a special package named Builtin. The Go compiler assumes that the Builtin package is always imported. Example functions include the following:
      • len(string) returns the length of the string.

      • panic(error) stops execution and displays an error message.

      • recover() manages the behavior of a panicking goroutine.

     

For more information, you can visit the official documentation of the builtin package at https://pkg.go.dev/builtin.

Installing Go

To install the Go compiler, visit https://go.dev/dl/ and download the binary release suitable for your system. Just run the installer to install the compiler on your system.

In order to verify your installation, on Windows OS, open the command prompt and type go version, as illustrated in Figure 2-1. The output shows the version of the Go compiler installed on your system.

A screenshot of a text reads the output version of the Go compiler installed on the system; it displays the g o 1.17.6 windows slash a m d 64.

Figure 2-1

Output showing the installed version of Go

If you type the path command at the command prompt, you can see where all the Go executable commands are saved. If you can see the path to the directory where Go is installed, e.g. C:UsersUserNamegoin, you are ready to build programs in Go.

To find the directory where Go is installed on macOS, open a terminal window and type export $PATH. Upon successful execution, a listing of the directory, indicating the path to the bin folder where Go was installed, e.g., user/local/go/bin, is shown. In addition, along with the addition of the command to execute Go programs to the directory phrase, a file is added to a path under the root. To ensure that you can run Go programs from anywhere, go to your home directory and type go version. If everything’s working correctly, you should see output indicating the version of the Go language that you’re using, as shown in Figure 2-2.

A screenshot displays the program to check the P A T H variables and the output indicating the version of the Go language which is used. The login information, path, and version are displayed.

Figure 2-2

Checking the PATH variable and version to ensure correct installation of Go Compiler

If you can see the version, you’re ready to start programming using the Go language. The first element you’ll want to explore is the Go Playground.

Go Playground

One of the fastest ways to start coding and developing Go-based programs is by using the Go Playground (https://go.dev/play/), a web-based IDE. As shown in Figure 2-3, without having to install anything, Go Playground allows users to edit, run, and experiment with the Go programming language. When the Run button is clicked, the Go Playground compiles and executes the Go code on Google servers and outputs the result of the execution.

A screenshot of a program displaying one of the fastest ways to start coding and a Go-based program using Go playground, a web-based I D E.

Figure 2-3

The Go Playground IDE

The Go Playground is a completely free-to-use service with no limitations. There are no requirements for user registration or licensing fees. Furthermore, it has no limitations on the number of source code files you can work with or the number of times you can run your code. It is a great way to test your Go code without creating or compiling any local source files. Nonetheless, there are other several IDEs available that can be used to develop Go applications.

Developing Go Applications Using IDEs

There isn’t a single integrated development environment (IDE) for good programming that’s endorsed or even developed by the Go development team. There are, however, many plug-ins for commercial and open-source IDEs that have been created by the Go community or by IDE vendors, which you choose mostly depending on what development environments you’re already familiar with. In this book, we use Go Playground and Visual Studio Code to write and run Go programs. Let’s jump into how to get started programming Go applications.

Getting Started Programming Go Applications

Once you’ve installed the Go development tools, you are ready to create as many Go applications as you like. Usually, a Go program is constructed of the following parts:
  • Declaring package name

  • Importing package(s)

  • Declaring and defining function(s)

  • Variable(s)

  • Expressions and statements

  • Comment(s)

Let’s Print Hello World!

Prior to learning about the Go programming language’s basic building blocks, knowing about the bare minimum structure of Go programs is important. Listing 2-3 illustrates how to print the "Hello World!" message on the screen as output. Even though the program is concise and has minimum functionality, it is adequate for understanding Go’s program structure.
package main
import (
    "fmt"
)
// Singe-Line Comment
/*
   Multi
   Line
   Comment
*/
func main(){
       fmt.Println("Hello, World ! ")
}
Listing 2-3

Basic Program that Illustrates Different Parts of a Go Program

Different parts of a Go program

Listing 2-3 explains the different parts of the following Go program:
  • Packages: In the first line of a Go program, packages are always mentioned. In Listing 2-3, the statement package main indicates the name of the package to which the program belongs. This statement is mandatory because, in Go, all programs are organized and run in packages. For any program, the starting point of execution is the main package. Furthermore, with every package, a path and name are associated.

  • Import: The import keyword is used to import different packages the application will use. It is a preprocessor directive that instructs the Go compiler to include all the files in the mentioned package. In the previous example, we imported the fmt (format) package, which provides different functions for formatting input and output.

  • Comments: In Go, double forward-slashes // are used to indicate a single-line comment in code. Multi-line comments are enclosed with the /* */ block.

  • Func: The func keyword is used to declare functions; in this case, a function named main. It is important to enclose the body of each function within curly braces {}. This is necessary for the Go compiler to know where each function starts and ends.

  • Main function: Execution starts at the main function in the main package, which makes the main identifier very important. The compiler will throw an error if the main function is excluded.

  • One of the built-in functions available in the fmt package is the Println(...) function. By importing the fmt package, you also export the Println method. The Println function is used to display output on the screen.

  • In Go, identifiers that are capitalized indicate that the particular identifier is exported, for example, the initial letter of the Println method is a capital letter. In Go, the term “exported means the function, constant, or variable can be accessed by the importer of that particular package.

How to Execute a Go Program

In order to execute the program, click the Run button on the Go Playground. Your code will be compiled and executed on Google servers and the output will be displayed on the screen.

To execute your program using the command prompt on Windows, you can use the go run filename.go command. For example, as in shown in Figure 2-4, the command used to run Listing 2-3 is go run main.go. To build applications, run the go build filename.go command. For example, to build the sample program, you’d issue the command go build main.go. To run the compiled version of the program, you’d issue the ./filename command, for example, ./main.

A screenshot displays the command used to run the listing. Go run main. go, and to build an application, run the go-build main. go command.

Figure 2-4

Commands to run and build a Go program

Interestingly, the compiled application runs faster than the go run command. Building the source code file will result in the creation of a compiled binary file specifically designed to work on your current operating system. Even though it is possible to compile your programs for other operating systems, by default, the compiler will generate the binary compatible with your operating system.

Keywords

Like every programming language, Go has a set of keywords (words reserved for special purposes). These keywords cannot be used as identifier names for contents, variables, functions, or any other purpose. Table 2-1 includes a list of Go’s keywords.
Table 2-1

Go’s Keywords

chan

const

goto

interface

struct

defer

case

func

var

select

break

for

go

import

continue

default

fallthrough

switch

type

range

if

else

return

package

map

Now that you are aware of its keywords, you are ready to learn how to use variables and other data structures in Go.

Variables

Like other programming languages, variables are used in Go to store data in memory. Being a statically-typed language, Go requires that each variable be assigned a type. Once a type is assigned it cannot be changed. There are two ways to set the type for a variable—explicitly or implicitly. By explicitly, we mean that at the time of declaration, the name of the type is mentioned. Implicitly means that, based on the variable’s initial value, the compiler will infer the type.

In Go, every variable should have a type. The type plays an important role in determining the layout and size of the variable’s memory, the allowed value ranges, and the set of operations applicable to the variable.

Variable Data Types

Go allows user-defined types and comes with various built-in data types. Here are a few basic built-in data types available in Go:
  • Boolean: Bool represents Boolean data in the form of true/false. True and false are the only two values that a Boolean data type can be assigned.

  • String: In Go, a variable of string type contains a series of characters.

  • Numeric data types:
    • Integers: Used to store whole numbers.
      • Fixed Integer: Included formats are uint8, uint16, uint32, int8, int16, and int 32. These are used to declare unsigned or signed integers. The numbers in the name of the formats are the bits that have an effect on the range of numbers that a variable can be assigned.

    • Float: Used to store floating-point numbers. Supported formats include float32 and float64.

    • Complex: Complex numbers contain two parts: the real number and imaginary number. Supported formats are complex64 and complex128.

    • Aliases: These can be used instead of full type names, such as byte (same as uint8), uint (32 or 64 bits), rune (same as int32), int (same as uint), and uintptr (an unsigned integer that is used to store uninterpreted bits of a pointer value).

  • Data collections: Go also has built-in types for different data collections.
    • Arrays and slices: Used to manage ordered data collections.

    • Maps and structs: Used to manage aggregations of values.

    • Enumeration: Used to store a set of named constant values.

  • Language organization types:
    • Function: Go considers function a type. This allows the passing of one function as an argument to another.

    • Interfaces: Used to specify a set of one or more method signatures.

    • Channels: Used to connect goroutines.

  • Data management:
    • Pointers: Store direct addresses of memory locations (variables).

Other than the built-in types, Go also allows programmers to create user-defined data types.

Variable Naming Conventions

A variable name can be made up of letters, digits, and underscores. However, the variable name should begin with either an underscore or a letter. The lower- and uppercase letters are treated differently, because of the case-sensitive nature of the Go language.

Declaring Variables

As Go is statically-typed, it requires you to set the type (explicitly or implicitly) of each variable used in your program during the compilation process. The assigned type cannot be changed at runtime.

Listing 2-4 illustrates the different ways of declaring and assigning a type to variables in Go. In the explicit declaration of a variable, the var keyword is used, after which the name and type of the variable are mentioned. For example, as shown in lines # 8 and 12, assigning an initial value is optional. Line#10 uses placeholders in the print function to print the values stored in the variables along with a message. Note that Printf doesn’t add the line feed automatically and hence we added the for a line feed.
package main
import(
       "fmt"
)
func main() {{
        //Explicit Declaration
        var aStringVariable string = "i am a string"
        fmt.Println(aStringVariable)
        fmt.Printf("Printing variable along text: %s ", aStringVariable)
        var anotherStringVariable string
        fmt.Println(anotherStringVariable)
        fmt.Printf("Printing variable along text: %s ", anotherStringVariable)
        var defaultInt int
        fmt.Println(defaultInt)
        //Implicit Declaration
        myString := "Implicit Declaration of string"
        fmt.Println(myString)
}
Listing 2-4

Ways to Declare Variables in Go

In the case of implicit declaration, the variable type is inferred based on the initial value assigned to the variable. Line#20 in Listing 2-4 illustrates implicit declaration. Note that the := operator only works for variable declarations inside of functions. The var keyword has to be used to declare variables outside the functions.

The var statement can also be used to declare a list of variables, as shown in Figure 2-5.

A listicle of a var statement that declares a list of variables in Go - chan, const, goto, interface, struct, defer, case, func, var, select, break, for, go, continue and default among some.

Figure 2-5

Different ways of declaring variables in Go

To declare constants, programmers must use the const keyword. Figure 2-6 illustrates how constants can be declared in a Go program.

A screenshot showcases the constants declared in a Go program. aconstantvariable int equals 64, and anotherconstantvariable int equals 64.

Figure 2-6

Declaring constants in Go

Constants are always declared outside of functions. If the name of the constant variable starts with a lowercase letter, that means it’s available for use only by the functions of the program (like the private keyword). The variable is public when its name begins with a capital letter.

Taking User Input

In the previous section, you learned about the use of the fmt package through which you can output different messages on the screen. This section demonstrates how to read user input from the console, store it in variables, or echo it to the screen. Note that standard input (stdin) is a stream that is used to read input data.

One important point to remember regarding Go Playground is that it does not support interactive programs and hence cannot read from os.Stdin. Due to this, it is better to use other IDEs, like Visual Studio or Code, to test the code in this book whenever user input is required.

In Go, the OS and IO packages contain different functions for reading the standard input from the console. These sets of functions are used to scan formatted text and extract values.
  • Scan, Scanf, and Scanln can be used to read input from os.Stdin.

  • Fscan, Fscanf, and Fscanln can be used to read input from a specified io.Reader.

  • Sscan, Sscanf, and Sscanln can be used to read input from an argument string.

Using scanf

In order to use the Scanf function, you must import the fmt package. It is used to specify which way the input should be read. The Scanf function scans the text input from stdin and stores the space-separated values into consecutive arguments as defined in the string format. The count of successfully scanned items is also returned by the Scanf function. If the returned number of items is less than the specified arguments, the function will throw an error. This is because it is mandatory for newline feeds in the input to match the newlines specified in the format. Listing 2-5 illustrates the use of the Scanf function for taking user input.
package main
import (
        "fmt"
)
func main() {
        var name string
        var age int
        fmt.Print("Enter Your name: ")
        fmt.Scanf("%s", &name)
        fmt.Println("Hello ", name)
        fmt.Print("Enter Your age: ")
        fmt.Scan(&age)
        fmt.Println("Your age is ", age)
}
Listing 2-5

Using the Built-In Scanf Function to Take User Input

Output:
Enter your name: Maryam
Hello Maryam
Enter your age: 30
Your age is 30

Using Scanln

Scanln scans the text from stdin; however, it stops scanning when a newline is encountered. Therefore, it is necessary to place a newline character or EOF after the final item to indicate the end of input. Listing 2-6 illustrates the use of Scanln.
package main
import (
        "fmt"
)
func main() {
        var name string
        var age int
        fmt.Print("Enter your name: ")
        fmt.Scanln(&name)
        fmt.Println("Hello ", name)
        fmt.Print("Enter your age: ")
        fmt.Scanln(&age)
        fmt.Println("Your age is ", age)
        var anInt int = 5
        var aFloat float64 = 42
        sum := float64(anInt) + aFloat
        fmt.Printf("Sum: %v, Type: %T ", sum, sum)
}
Listing 2-6

Using the Scanln Built-In Function to Take User Input in Go

/*Output:
Enter your name: Maryam
Hello Maryam
Enter your age: 30
Your age is 30*/

Scan vs Reader Functions

To split space-delimited tokens, you use the Scan functions and to read full lines, you used the reader function.

Using bufio

Buffered I/O is implemented via the bufio package. The bufio package wraps the io.Writer or io.Reader objects and returns a new Writer or Reader object, which has the necessary interface implemented for utility methods. This wrapping also provides textual I/O assistance and buffering.

In Listing 2-7, Line#11 declares two variables, one that stores the input. The second is an error object that stores any error messages.
 1   package main
 2   import(
 3       "bufio"
 4       "fmt"
 5       "os"
 6   )
 7
 8   func main(){
 9         reader := bufio.NewReader(os.Stdin)
10         fmt.Print("Enter Some Text: ")
11         input, _ := reader.ReadString(' ')
12         fmt.Println("You have entered: ", input)
13   }
Listing 2-7

Using the bufio Built-In Function to Take User Input in Go

Output:
Enter Text: Hi! I am the author
You entered: Hi! I am the author

As illustrated, if you want to ignore a variable in Go, you name it with an underscore.

Now that you know how to use variables and take user input in your Go programs, it is time to learn how to use different math operators in Go.

Math Operators and Packages

Go supports the same set of mathematical operators as other C-style languages, including the usual arithmetic operators, all the bitwise operators, assignment operators, and relational operators. Tables 2-2 through 2-6 list all of the operators in Go.
Table 2-2

List of Arithmetic Operators in Go

Arithmetic Operators

+

Sum operator is used for addition.

-

Difference operator is used for finding differences.

*

Multiply operator is used for finding products.

/

Quotient operator is used for finding the quotient after division.

%

Remainder operator is used for finding the remainder after division.

--

Decrement operator decreases one from the value of its operand and stores the result in the operand.

++

Increment operator increases one from the value of its operand and stores the result in the operand.

Table 2-3

List of Bitwise Operators in Go

Bitwise Operators

&

Binary AND operator. If the bit is present in both operands, the binary and operator copy it to the result.

|

Binary OR operator. If the bit is present in either of the operands, the binary or operator copies it to the result.

^

Bitwise XOR operator. If the bit is set in any operand, the bitwise xor operator copies the bit.

&^

Bit clear.

<<

Left shift operator. Shifts the left operand’s value to the left by the number of bits specified by the right operand.

>>

Right shift operator. Shifts the left operand’s value to the right by the number of bits specified by the right operand.

Table 2-4

List of Relational Operators in Go

Relational Operators

==

Checks if the two operands have equal values, if they do, it returns true.

!=

Checks if the two operands have unequal values, if they do, it returns true.

>

Checks if the left operand is greater than the right operand, if it is, it returns true.

<

Checks if the left operand is less than the right operand, if it is, it returns true.

<=

Checks if the left operand is less than or equal to the right operand, if it is, it returns true.

>=

Checks if the left operand is greater than or equal to the right operand, if it is, it returns true.

Table 2-5

List of Logical Operators in Go

Logical Operators

&&

Logical AND operator. Returns false is both operands are false and true if both the operands are true.

||

Logical OR operator. Returns true if any of the operands are true.

!

Logical NOT operator. Reverses the logical state of its operand, i.e., changes true to false and vice versa.

Table 2-6

List of Assignment Operators in Go

Assignment Operator

=

Simple assignment operator. The left operand is assigned the value of the right operand. For example, Z=X+Y will assign Z the calculated value of X+Y.

+=

Add and assignment operator. The value stored in the right operand is added to the value of the left operand and the result is assigned to the left operand.

For example, X += Y is equivalent to X = X + Y.

-=

Subtract and assignment operator. The value stored in the right operand is subtracted from the value of the left operand and the result is assigned to the left operand.

For example, X -= Y is equivalent to X = X – Y.

*=

Multiply and assignment operator. The value stored in the right operand is multiplied by the value of the left operand and the result is assigned to the left operand.

For example, X *= Y is equivalent to X = X * Y.

/=

Divide and assignment operator. The value stored in the right operand is divided by the value of the left operand and the result is assigned to the left operand.

For example, X /= Y is equivalent to X = X / Y.

%=

Modulus and assignment operator. The value stored in the right operand is divided by the value of the left operand and the remainder of the division is assigned to the left operand.

For example, X %= Y is equivalent to X = X % Y.

<<=

Left shift and assignment operator. The value of the left operand is shifted to the left by the number of times specified by the right operand.

For example, X <<= 1 is equivalent to X = X << 1.

 

>>=

Right shift and assignment operator. The value of the left operand is shifted to the right by the number of times specified by the right operand.

For example, X >>= 1 is equivalent to X = X >> 1.

&=

Bitwise AND assignment operator. For example, Y &= 3 is equivalent to Y=Y&3.

^=

Bitwise exclusive OR and assignment operator. For example, Y ^= 3 is equivalent to Y = Y ^ 3

|=

Bitwise inclusive OR and assignment operator. For example, Y |= 3 is equivalent to Y = Y | 2.

Note that Go doesn’t support implicit conversion of numeric types. This means that, for example, you cannot take an integer value and add it to a floating-point value without converting it.

Listing 2-8 contains two variables, one an integer and another a float. When you try to add them, this will result in the "invalid operation" error, crashing the application.
var anInt int = 5
var aFloat float32 = 42
sum := anInt + aFloat  //invalid operation
fmt.Printf("Sum: %v, Type: %T ", sum, sum)
Listing 2-8

Illustration of Go Not Supporting Implicit Conversion of Numeric Types

Output:

A screenshot of the output of invalid operation mismatched types int and float64 compiler (mismatched type).

In order to correctly perform this operation, you must convert one of these variables to match the type of the other. To achieve this goal, you have to wrap the value in the target type as a function call, as shown in Listing 2-9.
var anInt int = 5
var aFloat float32 = 42
sum := float32(anInt) + aFloat
fmt.Printf("Sum: %v, Type: %T ", sum, sum)
Listing 2-9

Explicit Conversion of Numeric Types in Go

Output:
Sum: 47, Type: float32

The Math Package

To perform different math operations, you use the Math package. It contains different functions and constants like pi. Go offers many tools for mathematical operations, operators and members of the math package, including functions and constants. Listing 2-10 illustrates the use of functions and constants from the Math package. For more information on the Math package, refer to the official documentation at https://pkg.go.dev/math.
package main
import (
        "fmt"
        "math"
)
func main() {
        i1, i2, i3 := 12, 45, 68
        intSum := i1 + i2 + i3
        fmt.Println("Integer Sum: ", intSum)
        f1, f2, f3 := 23.5, 65.1, 76.3
        floatSum := f1 + f2 + f3
        fmt.Println("Float Sum: ", floatSum)
        floatSum = math.Round(floatSum)
        fmt.Println("Rounded Sum is: ", floatSum)
        floatSum = math.Round(floatSum*100) / 100
        fmt.Println("Sum Rounded To Nearest 2 Decimals: ", floatSum)
        circleRadius := 15.6
        circumference := circleRadius * 2 * math.Pi
        fmt.Printf("Circumference: %.2f ", circumference)
}
Listing 2-10

Basic Program Illustrating the Use of the Math Package in Go

Dates and Times

In the Go language, the time package is used to manage the dates and times types. Declaring a variable with type as time encapsulates everything required for both times and dates type of data, which includes time zone management, math operations, and so on. Listing 2-11 illustrates the use of the time package.
package main
import (
        "fmt"
        "time"
)
func main() {
        now := time.Now()
        fmt.Println("Current Time is: ", now)
        formatDate := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
        fmt.Println("Go was launched at: ", formatDate)
        fmt.Println(formatDate.Format(time.ANSIC))
        parsedTime, _ := time.Parse(time.ANSIC, "Tue Nov 10 23:00:00 2009")
        fmt.Printf("The type of parsedTime is %T ", parsedTime)
}
Listing 2-11

Basic program Illustrating the Use of the Time Package in Go

Output:
Current Time is:  2022-08-29 21:06:14.02792 +0500 PKT m=+0.003998301
Go was launched at:  2009-11-10 23:00:00 +0000 UTC
Tue Nov 10 23:00:00 2009
The type of parsedTime is time.Time

Listing 2-11 illustrates the use of the time package for using date and time types. In the example, in Line#10, a variable named now is declared. It will be initialized using the built-in time.Now() function, which returns a timestamp containing the current date, time, and time zone. It is also possible in Go for users to create their own explicit date and time values. Line#14 declares a variable named formatDate, which is initialized using the time.Date() function. The Date() function returns the time in the appropriate zone according to a given location corresponding to the format yyyy-mm-dd hh:mm:ss + nsec nanoseconds. You can also use a formatted version of the daytime value, as shown in Line#18. The Format() function returns a textual representation of the time value formatted as per the layout defined by the argument. This example uses the ANSIC format, but you can refer to the official documentation at https://pkg.go.dev/time#Time.Format for other standard formats.

In Listing 2-12, the Parse() function returns the time value representation of a string after parsing it.
parsedTime, _ := time.Parse(time.ANSIC, "Tue Nov 10 23:00:00 2009")
fmt.Printf("The type of parsedTime is %T ", parsedTime)
Listing 2-12

Using the Parse Function from the Time Package

Output:
The type of parsedTime is time.Time

Since the Parse() function can return an error object, in this example, we added an underscore to ignore the object. The time object has many other useful functions. For example, you can add dates and perform special kinds of formatting, and so on. There are also patterns available that give you complete flexibility in how to format daytime values. The functions and constants in the time package give you all the tools you need to store and manage the date and time values. For more on this topic, refer to the official documentation on the time package at https://pkg.go.dev/time.

Operators Precedence in Go

Operator precedence refers to the criteria that determine the grouping of terms in an expression. It also affects the result of the expression evaluation. Like other programming languages, in Go, there is a pre-defined order of precedence of the operators, where some have higher precedence over the others. For example, the division operator / has higher precedence over the subtraction operator -.

Consider the statement a = 56 + 4 * 8 Here, variable a is assigned the value 88, not 480. This is because the multiply operator * has higher precedence over the addition operator +. Therefore, 4 * 8 is multiplied first and then the obtained result is added to 56.

Table 2-7 lists the operators from highest to lowest precedence. In a given expression, after scanning it from left to right, first, the operators having higher precedence are evaluated.
Table 2-7

List of Operator Precedence in Go

Category

Operator

Associativity

Postfix

( ) [ ] -> . ++ - -

Left to right

Unary

+ - ! ~ ++ - - (type)* & sizeof

Right to left

Multiplicative

* / %

Left to right

Additive

+ -

Left to right

Shift

<< >>

Left to right

Relational

< <= > >=

Left to right

Equality

== !=

Left to right

Bitwise AND

&

Left to right

Bitwise XOR

^

Left to right

Bitwise OR

|

Left to right

Logical AND

&&

Left to right

Logical OR

||

Left to right

Assignment

= += -= *= /= %=>>= <<= &= ^= |=

Right to left

Comma

,

Left to right

Memory Management and Reference Values

The Go runtime is required to run Go applications using the go run command on your system. The runtime is also included in the compiled and build binary Go application. Either way, like in any marked language, such as Java and C#, Go applications rely on the runtime that operates silently in the background utilizing dedicated threads for memory management. The advantage of the runtime is the elimination of the need to explicitly duplicate or allocate memory in the code.

New vs Make

It is critical to ensure that complex types, such as maps, are appropriately initialized. To initialize the complex objects in the Go programming language, there are two built-in functions—make and new. Be careful using them, as there is a difference between the two.

The new function only allocates memory for the complex object but does not initialize the memory. When an object, say a map, is allocated using the new function, it only returns the memory address of where that map is located. However, there is zero memory storage associated with the map object. Therefore, when a key-value pair is added to the map, it will throw an error.

Contrary to this, the make function allocates as well as initializes the memory for the map object created using the make function. It returns the memory address, like the new function, and the storage is initialized to non-zero. This means the map object can accept values without throwing errors.

Incorrect Memory Allocation Example

Listing 2-13 illustrates the incorrect memory allocation of map objects. A map object named string_map is declared on Line#1 using the new function. The map object accepts keys as strings and the associated values as integers. On Line#2, a key-value pair is added as an entry to the declared map object, where the key is named Marks and the associated value is 56.
string_Map := new(map[string]int)
string_Map["Marks"] = 56
fmt.Println(string_Map)
Listing 2-13

Illustration of Incorrect Memory Allocation of Map Objects

At runtime, Listing 2-13 will make the application crash and an error is thrown, as shown in Figure 2-7. This error is raised because you are attempting to place data in a map that does not have any memory storage initialized.

A screenshot of an output error raised because of attempting to place data in an amp that does not have any memory storage initialized stating invalid operation.

Figure 2-7

Error raised due to incorrect memory allocation of map object

Correct Memory Allocation Example

The correct way to allocate memory for a complex object like the map is to warp your declaration in the make function, as shown in Listing 2-14. The make function will also initialize non-zero storage memory for the object.
string_Map := make(map[string]int)
string_Map["Marks"] = 56
fmt.Println(string_Map)
Listing 2-14

Illustration of Correct Memory Allocation of Map Object

This time, when you initialize the object with the make function and try to add new entries to the map object, the code will successfully compile and run without errors, as shown in the output in Figure 2-8. Remember that when using complex objects, it is highly important to use the make function for initialization if you need to add data to the object immediately.

A screenshot of the code after the new entries to the map object. The code reads go run mem-allocation-eg.go. map open parenthesis marks:56 close parenthesis.

Figure 2-8

Output after correct memory allocation

Memory Deallocation

Memory deallocation is performed in an autonomous fashion by the garbage collector (GC) that comes packaged with the Go runtime. When the garbage collector is triggered while working in the background, it searches for objects that are either out of scope or set to nil, so it may clear away the memory and return it to your memory pool.

In Go version 1.5, the garbage collector was completely reconstructed to have very low latency for effectively reducing the number of pauses that occur while running Go applications. In the latest version, 1.18.3, the performance of the garbage collector while performing memory allocation is much faster and is also almost unnoticeable even on slower computers. For more information on garbage collectors, refer to the official documentation on runtime at https://pkg.go.dev/runtime and the talk release of the garbage collector improvement at https://talks.golang.org/2015/go-gc.pdf.

Pointers Data Type

Every variable is essentially a memory location. Note also that each memory location has a defined address as well. In Go, the ampersand (&) operator indicates a memory address and is used to access the address of a variable.

What Is a Pointer?

Pointers are in essence variables that have the capability of storing memory addresses of other variables. Note that a memory address refers to the direct address of the variable. A pointer can be declared with any data type, however, initializing it, i.e., storing the address of another variable, is not necessary. Similar to high-level languages, Go also supports pointers. Pointers are useful in Go to perform different tasks, for example, without the use of pointers you cannot perform a call by reference.

Declaring Pointers in Go

It is necessary to declare a pointer before its use, like any other variable or constant. The general syntax for declaring a pointer variable in Go is var variable_name *variable-type. Here, variable-type indicates the base type of the pointer. Make sure that the selected base type is a valid Go data type. variable-name indicates the pointer’s name. One of the most important things that differentiates pointer declarations from other variable declarations is the asterisk (*) operator. In this statement, the asterisk indicates that the declared variable is a pointer. Listing 2-15 illustrates some valid examples of pointer declarations.
var intPointer  *int         /* pointer to an integer variable */
var floatPointer  *float32   /* pointer to a float variable */
var strPointer *string       /* pointer to a string variable */
Listing 2-15

Different Ways of Declaring Pointers in Go

It should be noted that, irrespective of its type, the inherent data type of value stored in all pointers is the same, i.e., a long hexadecimal number. This represents memory addresses, as all addresses are in the long hexadecimal format. The data type of the variable or constant that a pointer points to is the only differentiation between pointers of different data types. Furthermore, the value of the pointer can also be set to nil manually in the case of strings and automatically if the pointer is not initialized at the time of declaration.

Example

Let’s learn more about pointers through an example program, shown in Listing 2-16.
var intPointer  *int         /* pointer to an integer variable */
fmt.Println("Value of intPointer: ", *intPointer)
Listing 2-16

Declaring Pointers

In Listing 2-16, the first line declares a pointer named intPointer with int type. This is a correct way of declaration; however, if you try to print the contents of intPointer in the second line, this will throw a runtime error and the application will crash, as shown in Figure 2-9. This happens because intPointer is not currently initialized to point at anything and is hence nil.

A screenshot showcases the output of a program. The first line declares a pointer named int pointer with int type. The content of the int pointer in the second line shows a runtime error and the application crash.

Figure 2-9

Output of printing a nil pointer

Let’s modify Listing 2-16 to ensure that the pointer variable is pointing to a valid variable. As illustrated in Listing 2-17, we use the := operator to explicitly declare a pointer and use the ampersand & operator to make it a pointer to another variable.
package main
import (
        "fmt"
)
func main() {
        value1 := 42
        var pointer1 = &value1
        fmt.Println("Value of pointer1: ", *pointer1)
        value2 := 42.13
        pointer2 := &value2
        fmt.Println("Value1: ", *pointer2)
        value3 := 32.5
        pointer3 := &value3
        *pointer3 = *pointer3 / 31
        fmt.Println("Pointer3: ", *pointer3)
        fmt.Println("Value3: ", value3)
}
Listing 2-17

Program Showing Correct Initialization of Pointers

//Output:
/*
Value of pointer1: 42
Value1: 42.13
Pointer3: 1.0483870967741935
Value3: 1.0483870967741935
*/

In the main function, on Line#1, we declare an integer variable with 42 as its value. On Line#2, the ampersand (&) operator is used to assign the direct memory address of the variable value1 to the variable pointer1. Since you are using an ampersand (&) operator, the Go compiler will automatically recognize that pointer1 is a pointer variable used for storing memory addresses. Now, if you print the value stored at the memory pointed by the pointer pointer1, it will be the same as that stored in value1, i.e., 42. In order to print the value stored where the memory address points, the asterisk (*) operator is used, as shown in Lines #3 and #8 of the main function. Similarly, you can use pointers to point to floating-point variables as well. Note that pointer1 is declared and assigned value implicitly, whereas the pointer2 is declared explicitly with a data type.

You can also change the values of pointed variables through the pointers, as shown in Listing 2-17:
*pointer1 = *pointer1 / 31
fmt.Println("Pointer1: ",*pointer1)
fmt.Println("Value1: ", value1)
Output:
Pointer3:  1.0483870967741935
Value3:  1.0483870967741935

On Line#1, you access the value stored at the memory location pointed by pointer1 using the asterisk (*) operator, dividing that value by 31 and storing the answer in the same location. As seen in the output, the pointer1 and value1 variables have the same value.

Comparison with Java and C-Style Languages

As in Java or C#, if you have an original variable and a reference variable (pointer) that points to that variable, you can change that value of the original variable either by changing the original variable or by changing the variable that’s pointing to it. However, unlike in Java, the pointer doesn’t have to point at any particular value initially, and you can change it at runtime to point to another value. On the other hand, you’ll find pointers in Go to be very similar and just as valuable in C, C#, and other similar languages.

Ordered Values in Arrays and Slices

This section discusses the arrays data structure and the slice, which is an abstraction of arrays.

Arrays in Go

As with other C-style languages, Go supports the array data structure. Arrays in Go are used to store sequential fixed-size collections of elements, noticeably of the same type. Even though an array is usually referred to as a storage of a collection of items, it can also be considered a collection of variables having identical types. Furthermore, arrays are consecutive memory locations, whereby the first element of the array corresponds to the lowest address and the last element to the highest address. It is also possible to access any particular element of an array through the index number. The index numbers of an array range from 0 to size-1, as illustrated in Figure 2-10. As per the figure, to access the element in the second index (i.e., the third element), you use array_name[2], which would yield the value 11.

A tabular representation shows the arrays in Go. It has access to elements 1, 2, 3, 4 to n and index 0, 1, 2, 3 to n.

Figure 2-10

Arrays in Go

Declaring Arrays

The general format for declaration of an array in Go is var array_name [SIZE] data_type. Note that you must mention the data_type, as it indicates the type of elements the array will be holding. Also, the total count of elements (SIZE) is also required, as it indicates the size of the array. Arrays declared in this format are known as single-dimensional arrays. Note that Size should be an integer constant value that is greater than zero. Additionally, any valid Go type can be used to specify the data_type, i.e., the type of elements that the array can hold. For example, the statement var floatArray [10] float32 declares an array of ten elements called floatArray of data_type as float32.

Initializing Arrays

Arrays in Go can be initialized element at a time or using a single statement, as illustrated in Listing 2-18. Remember, the number of items specified between the curly braces { } should not exceed the size of the array specified between the square brackets [ ], as illustrated by Listing 2-18.
var floatArray = [5]float32{10.0, 200.0, 35.64, 78.0, 540.60}
Listing 2-18

Initializing Arrays with Size Specified in Go

If the size of the array is omitted, the Go compiler automatically sets the size of the array equal to the number of items specified in the initialization statement. For example, Listing 2-19 will create an array of size 5 as the array is initialized with five values.
var floatArray = []float32{10.0, 200.0, 35.64, 78.0, 540.60}
Listing 2-19

Initializing Arrays Without Specifying Size in Go

Accessing Array Elements

Individual elements of an array can be accessed through the use of indexes with array names. This is achieved by wrapping the index number of the required element within square brackets [], placed right after the name of the array. For example, to assign value to a single element of an array, the following statement can be used
balance[4] = 50.0
This statement assigns the value on the right side of the assignment operator to the element at the forth index of the array named balance. As the index number in arrays starts with 0, this statement assigns the value 50.0 to the fifth element in the array. Note, the first index of an array is also known as the base index, whereas the last index will always be arraySize-1. Also note that the = operator is used to assign values to individual elements and not the := operator, as the data type of the array has already been defined. Figure 2-11 is a pictorial representation of the concepts discussed here.

A tabular representation of the concept of data stored in arrays. The balance of elements from 0 to 4 is 1000.0, 2.0, 3.4, 7.0, and 50.0.

Figure 2-11

Data storage in arrays

Example

Listing 2-20 shows an example that includes all of the concepts discussed so far.
package main
import (
        "fmt"
)
func main(){
        /*declaring and initializing an array "array1" of size 3 and type as floating-point*/
        var array1 = []float32{10.5, 5.2, 2.88}
        var array2 [10]int
        var i, j int
        //Initializing elements of the array
        for i =0; i < 10; i++{
               array2[i] = i + 50 //setting element at location i to i+50
        }
        //Print the value of each elements of array1
        fmt.Println("Elements stored in Array1")
        for j =0; j <3; j++{
               fmt.Printf("Element[%d] = %f ", j, array1[j])
        }
        //Print the value of each elements of array2
        fmt.Println("Elements stored in Array2)
        for j =0; j <10; j++{
               fmt.Printf("Element[%d] = %d ", j, array2[j])
        }
        fmt.Println("Size of array1: ", len(array1))
        fmt.Println("Size of array2: ", len(array2))
}
Listing 2-20

Basic Program Illustrating the Use of Arrays in Go

/*Output:
$ go run array-1.go
Elements stored in Array1
Element[0] = 10.500000
Element[1] = 5.200000
Element[2] = 2.880000
Elements stored in Array1
Element[0] = 50
Element[1] = 100
Element[2] = 150
Element[3] = 200
Element[4] = 250
Element[5] = 300
Element[6] = 350
Element[7] = 400
Element[8] = 450
Element[9] = 500 */

Querying the Size of an Array

You can find the number of items that an array can hold (i.e., its size) by using the built-in len (short for length) function. You wrap the array identifier in the len function, like len(array_name). Listing 2-21 demonstrates the use of the len function.
fmt.Println("Size of array1: ", len(array1))
fmt.Println("Size of array2: ", len(array2))
Listing 2-21

Using the len Function with Array in Go

Output:
Size of array1:  3
Size of array2:  10

Remember that, in Go, an array is an object, and like all objects, if you pass it to a function, a copy will be made of that array. However, storing the data is just about all you can do with the arrays and it’s not easy to sort or add/remove items at runtime. For such features and other operations, you should package the ordered data in slices instead of arrays.

Slices in Go

In Go, a slice is a layer of abstraction placed on top of the array data structure. The runtime allocates the necessary memory and constructs the array in the background when you define a slice, but only returns the slice. Furthermore, similar to arrays, all elements of a slice are of the same type. Even though arrays can simultaneously hold multiple data items of the same kind, there is no way to dynamically increase its size or extract a sub-array from it using any built-in functions. Slices get around these drawbacks. Slices are commonly utilized in Go and they provide various utility methods that are required with arrays.

Defining Slices

Slices are declared the same way as an array, with the difference that you do not specify their size, as shown in Listing 2-22. Alternatively, the make function can also be used to create slices.
var colors = []string{"ed", "Green", "Blue"}
var marks []float32          /* slice of unspecified size */
var marks = make([]int,5,5)  /* slice of length and capacity 5*/
Listing 2-22

Defining Slices in Go

The len( ) and cap( ) Functions

Being an abstraction placed on top of arrays, the underlying structure of slices are arrays. To query the total number of items present in a slice, you use the len() function. To query the total number of items that can be stored in a slice, you use the cap() function. This essentially returns the size of the slice. Listing 2-23 illustrates the use of slices in Go.
package main
import (
        "fmt"
)
func main() {
        var marks = make([]float64, 3, 5) //declaring slice of length 3 and capacity 5
        printItemsOfSlice(marks)          //pass slice to a function
}
// function that accepts a slice and prints its details
   func printItemsOfSlice(x []float64) {
        fmt.Printf("Length=%d Capacity=%d Slice=%v ", len(x), cap(x), x)
}
Listing 2-23

Example Illustrating the Use of the len() and cap() Functions

/*Output:
s
*/

Nil Slice

A slice is initialized as nil by default if it is declared with no inputs. Also, the length and capacity of such a slice is zero. Listing 2-24 demonstrates the concept of nil slices in Go.
package main
import "fmt"
func main() {
        var marks []float64       //declaring slice of float type
        printItemsOfSlice(marks)  //pass slice to a function
        if marks == nil {
                fmt.Printf("Slice is Nil")
        }
}
// function that accepts a slice and prints its details
   func printItemsOfSlice(x []float64) {
        fmt.Printf("Length=%d Capacity=%d Slice=%v ", len(x), cap(x), x)
}
Listing 2-24

Nil Slices in Go

/*Output:
Length=0 Capacity=0 Slice=[]
Slice is Nil
*/

Sub-Slicing in Go

In Go, you can extract a sub-slice of an array by specifying the required lower and upper bounds using [lower-bound:upper-bound]. Listing 2-25 illustrates the concept of sub-slicing in Go.
package main
import "fmt"
func main() {
        /* Declare and Initialize a Slice of float type */
        marks := []float32{10, 12.6, 20.0, 37.56, 48.74, 50.0, 64.15, 79.63, 8.75}
        /*pass slice to user-defined function to print it details*/
        printSliceDetails(marks)
       /* Printing the Elements of the Original Slice */
       fmt.Println("Original Slice =", marks)
        /* Printing sub-slice of marks slice beginning from
           index 1(inclusive) to index 5(excluded)*/
        fmt.Println("Marks[1:5] = ", marks[1:5])
        /* Not indicating the lower bound inferred as 0 */
        fmt.Println("Marks[:4] =", marks[:4])
        /* Not indicating the upper bound is inferred as len(slice) */
        fmt.Println("Marks[3:] =", marks[3:])
        marks1 := make([]float32, 0, 5)
        /*pass slice to user-defined function to print it details*/
        printSliceDetails(marks1)
        /* Printing sub-slice of marks beginning from
           index 0(inclusive) to index 3(exclusive) */
        marks2 := marks[:3] //storing subslice in slice named marks2
        /*pass slice to user-defined function to print it details*/
        printSliceDetails(marks2)
        /* Printing sub-slice of marks beginning from
           index 4(inclusive) to index 5(exclusive) */
        marks3 := marks[4:5]
        /*pass slice to user-defined function to print it details*/
        printSliceDetails(marks3)
}
func printSliceDetails(x []float32) {
        fmt.Printf("Length=%d Capacity=%d Slice=%v ", len(x), cap(x), x)
}
Listing 2-25

Sub-Slicing in Go

/*Output:
Length=9 Capacity=9 Slice=[10 12.6 20 37.56 48.74 50 64.15 79.63 8.75]
Original Slice = [10 12.6 20 37.56 48.74 50 64.15 79.63 8.75]
Marks[1:5] = [12.6 20 37.56 48.74]
Marks[:4] = [10 12.6 20 37.56]
Marks[3:] = [37.56 48.74 50 64.15 79.63 8.75]
Length=0 Capacity=5 Slice=[]
Length=3 Capacity=9 Slice=[10 12.6 20]
Length=1 Capacity=5 Slice=[48.74]
*/

The append( ) and copy( ) Functions

In Go, the append() function increases the capacity of a slice. The copy() function copies contents of a source slice into a destination slice. Listing 2-26 shows the use of these two functions in Go.
package main
import "fmt"
func main() {
        /* Declaring a slice of int type */
        var nums []int
        /* pass slice to user-defined function to print details */
        printSliceDetails(nums)
        /* Nil slice are allowed with Append */
        nums = append(nums, 10)
        printSliceDetails(nums)
        /* Adding one element to the slice */
        nums = append(nums, 100)
        printSliceDetails(nums)
        /* At one time adding more than one element to the slice */
        nums = append(nums, 1000, 10000, 100000)
        printSliceDetails(nums)
        /* Creating a slice nums1 that has double the capacity of nums slice*/
        nums1 := make([]int, len(nums), (cap(nums))*2)
        /* Copy the elements stored in nums into nums1 */
        copy(nums1, nums)
        /* pass slice to user-defined function to print details */
        printSliceDetails(nums1)
        var colors = []string{"Red", "Green", "Blue"}
        fmt.Println("Before: ", colors)
        colors = append(colors[1:len(colors)])
        fmt.Println("Items after removig 1st element:", colors)
}
func printSliceDetails(x []int) {
        fmt.Printf("Length=%d Capacity=%d Slice=%v ", len(x), cap(x), x)
}
Listing 2-26

Using the Append and Copy Functions in Go

/*Output:
Length=0 Capacity=0 Slice=[]
Length=1 Capacity=1 Slice=[10]
Length=2 Capacity=2 Slice=[10 100]
Length=5 Capacity=6 Slice=[10 100 1000 10000 100000]
Length=5 Capacity=12 Slice=[10 100 1000 10000 100000]
*/
To remove items from a slice, you can also use the append() function, but this time indicate the range in the format of two numbers separated by a colon wrapped in parentheses, as shown in Listing 2-27.
var colors = []string{"Red", "Green", "Blue"}
fmt.Println("Before: ", colors)
colors =  append(colors[1:len(colors)])
fmt.Println("Items after removing 1st element: ", colors)
Listing 2-27

Using the Append Function to Remove Items from a Slice

Output:
Before:  [Red Green Blue]
Items after removing 1st element: [Green Blue]

In Listing 2-27, the first item is removed because that range is telling it to start with the item in array at index one, which is the second item.

However, in the colors = append(colors[:len(colors)-1]) statement, if the starting index in the range is eliminated and instead you pass colors[:len(colors)-1], the last item will be removed from the slice.

Sorting Slices

As shown in Listing 2-28, you can also perform sorting on slices using the sort package in Go. The sort function has multiple functions for sorting different data types. By default, the slice will be sorted in numerical order from lowest to highest using the sort() function. For more information, refer to the official developer documentation on the sort package at https://pkg.go.dev/sort. Note that, using the sort() function, you can also sort user-defined data collections.
package main
import (
        "fmt"
        "sort"
)
func main() {
        intSlice := make([]int, 5)
        intSlice[0] = 6
        intSlice[1] = 99
        intSlice[2] = 45
        intSlice[3] = 34
        intSlice[4] = 1
        fmt.Println("Original Slice: ", intSlice)
        sort.Ints(intSlice)
        fmt.Println("Sorted Slice: ", intSlice)
}
Listing 2-28

Using Sort Function to Sort Slices

/*Output:
Original Slice:  [6 99 45 34 1]
Sorted Slice:  [1 6 34 45 99]
*/

Maps

A map, in Go language, can be considered an unordered collection of key-value pairs. In other terms, a map can be considered a hash table that allows storage of collections of data from which values can then be retrieved arbitrarily based on their keys.

Note that, as the keys are compared for sorting purposes, a map’s keys should be of any type that’s comparable. However, it’s a usual practice of using string type for keys and any other valid type for the associated values.

Defining Maps

The make() function allocates and initializes a non-zero storage when used to declare complex data types. Therefore, it is a must to use the make() function to declare a map object. The general format for declaring a map is as follows
map_variable := make(map[key_data_type]value_data_type)

Adding Entries to a Map Object

To add an entry into the map object, use this general format: map_variable_name[key] = value. Note that because the map is an unordered collection of items, the order in which the items will be displayed is not always guaranteed.

Deleting Entries from a Map Object

In Go, to delete a map’s entry (i.e., a key-value pair stored in the map), you use a built-in function called delete(). The delete() function takes two arguments—the map whose entry is to be deleted and the key to delete. The general format to achieve this is delete(map_variable, key).

Iterating Over Stored Values in a Map Object

To iterate over the key-value pairs stored in a map, you can use the For loop statement, as illustrated in Listing 2-29. Here, the key is a variable to store the keys and the value is another variable to store the corresponding value. At every iteration, the key variable will hold only one of the keys stored in the map, and the value variable will hold its corresponding value. Note that key and value are both user-defined variable names.
for key, value := range map_variable_name{
       /*perform desired operation - e.g. print all stored key-value pair */
       fmt.printf("%v: %v ", key, value)
}
Listing 2-29

Using the For Loop to Iterate Over a Map

Just like the display order of a map, its iteration order is also not guaranteed. It is up to the user to manage it themselves if they want to guarantee the order. Users must list all the items stored in alphabetical order by using slices. This can be achieved by extracting the keys from the map as slices of the strings array, as illustrated in Listing 2-30.
package main
import (
        "fmt"
        "sort"
)
func main() {
        states := make(map[string]string)
        states["WA"] = "Washington"
        states["NY"] = "New York"
        states["CA"] = "California"
        keys := make([]string, len(states))
        i := 0
        for key := range states {
                keys[i] = key
                i++
        }
        fmt.Println("Order of Keys Before Sorting: ", keys)
        sort.Strings(keys)
        fmt.Println("Order of Keys After Forting: ", keys)
}
Listing 2-30

Displaying Items of a Map

/*Output:
Order of Keys Before Sorting: [WA NY CA]//the display order is not guaranteed
Order of Keys After Forting: [CA NY WA]//the display order is guaranteed
*/

The first line declares a slice named keys of string type with a size equal to that of the map object named states. Then a for loop is used to iterate over the keys stored in the map object and store them into the keys slice. After that, to sort the keys slice, you use the sort() function that is available in the sort package. In this example, the initial key-value pairs stored in the states map were [CA:California NY:New York WA:Washington]

Using the technique shown in Listing 2-30, you can ensure that the order will be the same. Each time you iterate through a slice using the range keyword, you will get an integer representing the current index in the slice. Now you can use an integer instead to iterate over the keys slice, as shown in Listing 2-31.
for i := range keys {
    fmt.Println(states[keys[i]])
    //outputs the value stored in the states map at the passed key
}
Listing 2-31

Using a For Range to Iterate Over a Map

Output:
California
New York
Washington

Example

Listing 2-32 includes all of the concepts discussed previously related to how to use maps in Go to store unordered data collections in memory and then access the items arbitrarily using their keys. It also shows how to use slices and maps together to process the stored data in a desired order.
package main
import (
        "fmt"
        "sort"
)
func main() {
        states := make(map[string]string)
        fmt.Println(states)
        states["WA"] = "Washington"
        states["OR"] = "Oregon"
        states["CA"] = "California"
        fmt.Println(states)
        california := states["CA"]
        fmt.Println(california)
        delete(states, "OR")
        states["NY"] = "New York"
        fmt.Println(states)
        for key, value := range states {
                fmt.Printf("%v: %v ", key, value)
        }
        keys := make([]string, len(states))
        i := 0
        for key := range states {
                keys[i] = key
                i++
        }
        fmt.Println(keys)
        sort.Strings(keys)
        fmt.Println(keys)
        for i := range keys {
              fmt.Println(states[keys[i]])
        }
}
Listing 2-32

Example Illustrating the Use of Map in Go

/*Output:
map[]
map[CA:California OR:Oregon WA:Washington]
California
map[CA:California NY:New York WA:Washington]
WA: Washington
NY: New York
CA: California
[WA NY CA]
[CA NY WA]
California
New York
Washington
*/

Structs Data Type

As discussed earlier, arrays in Go are essentially variables that can store multiple data items of the same kind. To overcome this limitation, Go offers structures, which are user-defined types that can be used to store data items of different kinds. A structure essentially represents a record of data. For example, if there is a need to track different information about books available in a library, structures are the most useful way to store different attributes of a book, such as BookID, Title, Subject, Author, and so on.

Defining a Structure

To define a structure, you must use the type and struct statements. The struct statement defines a new data type that will contain multiple members. The type statement binds a name to the structure. The general format for declaring a structure using the type and struct statements is illustrated in Listing 2-33.
type struct_variable_type struct {
   member definition;
   member definition;
   ...
   member definition;
}
Listing 2-33

General Format for Declaring Structures in Go

After defining a structure type in your code, multiple variables of this struct type can be declared. The general format for doing so is variable_name := structure_variable_type {value1, value2...value n}

Accessing Members of a Structure

In order to access any member field of a struct, you use the member access operator (.). Remember that the struct keyword is mandatory for defining variables of a defined struct type. Listing 2-34 illustrates the use of the struct type in Go and shows how to access its member fields.
package main
import "fmt"
type Books struct {
        bTitle      string
        bAuthorName string
        bSubject    string
        book_id     int
}
func main() {
        var Book1 Books /* Declaring variable of type Book */
        var Book2 Books /* Declaring variable of type Book */
        /* Access member fields of Book struct to define Book1 */
        Book1.bTitle = "The Go Programming Language"
        Book1.bAuthorName = "Alan A. A. Donovan and Brian W. Kernighan"
        Book1.bSubject = "A complete guide to Go programming"
        Book1.book_id = 6495
        /* Access member fields of Book struct to define Book2 */
        Book2.bTitle = "The Complete Book of Arts & Crafts"
        Book2.bAuthorName = "Dawn Purney"
        Book2.bSubject = "The Complete Book of Arts and Crafts of fun activities for children"
        Book2.book_id = 6496
        /* Print the details of Book1 */
        fmt.Printf("Book1 bTitle : %s ", Book1.bTitle)
        fmt.Printf("Book1 bAuthorName : %s ", Book1.bAuthorName)
        fmt.Printf("Book1 bSubject : %s ", Book1.bSubject)
        fmt.Printf("Book1 book_id : %d ", Book1.book_id)
        /* Print the details of Book1 */
        fmt.Printf("Book2 bTitle : %s ", Book2.bTitle)
        fmt.Printf("Book2 bAuthorName : %s ", Book2.bAuthorName)
        fmt.Printf("Book2 bSubject : %s ", Book2.bSubject)
        fmt.Printf("Book2 book_id : %d ", Book2.book_id)
}
Listing 2-34

Using Structs in Go

/*Output:
Book1 bTitle      : The Go Programming Language
Book1 bAuthorName : Alan A. A. Donovan and Brian W. Kernighan
Book1 bSubject    : A complete guide to Go programming
Book1 book_id     : 6495
Book2 bTitle      : The Complete Book of Arts & Crafts
Book2 bAuthorName : Dawn Purney
Book2 bSubject    : The Complete Book of Arts and Crafts of fun activities for children
Book2 book_id     : 6496
*/

Passing Structures as Function Arguments

In Go, just like with any other variable or pointer, you can also pass a structure as a function argument. Listing 2-35 illustrates how to pass structures as function arguments.
package main
import "fmt"
type Books struct {
        bTitle      string
        bAuthorName string
        bSubject    string
        book_id     int
}
func main() {
        var Book1 Books /* Declaring variable of type Book */
        var Book2 Books /* Declaring variable of type Book */
        /* Access member fields of Book struct to define Book1 */
        Book1.bTitle = "The Go Programming Language"
        Book1.bAuthorName = "Alan A. A. Donovan and Brian W. Kernighan"
        Book1.bSubject = "A complete guide to Go programming"
        Book1.book_id = 6495
        /* Access member fields of Book struct to define Book2 */
        Book2.bTitle = "The Complete Book of Arts & Crafts"
        Book2.bAuthorName = "Dawn Purney"
        Book2.bSubject = "The Complete Book of Arts and Crafts of fun activities for children"
        Book2.book_id = 6496
        /* Print the details of Book1 by passing it as an argument to function */
        printBookDetails(Book1)
        /* Print the details of Book2 by passing it as an argument to function */
        printBookDetails(Book2)
}
func printBookDetails(book Books) {
        fmt.Printf(" Title : %s ", book.bTitle)
        fmt.Printf("Authors : %s ", book.bAuthorName)
        fmt.Printf("Subject : %s ", book.bSubject)
        fmt.Printf("Book ID : %d ", book.book_id)
}
Listing 2-35

Passing Structures as Function Arguments in Go

/*Output:
Title   : The Go Programming Language
Authors : Alan A. A. Donovan and Brian W. Kernighan
Subject : A complete guide to Go programming
Book ID : 6495
Title   : The Complete Book of Arts & Crafts
Authors : Dawn Purney
Subject : The Complete Book of Arts and Crafts of fun activities for children
Book ID : 6496
*/

Pointers to Structures

Just as you can define pointers that can be used for pointing (storing) to other constants or variables, you can also define pointers to structures in Go. For example, a pointer variable named struct_pointer of the type Books can be declared using the var struct_pointer *Books statement. The struct_pointer variable can then be used to store addresses of structure variables. The ampersand (&) operator gets the direct address of a structure variable, for example, struct_pointer = &Book1. Furthermore, the member access operator (.) accesses the members of a structure where the pointer is pointing, such as struct_pointer.title

Example

Let’s rewrite Listing 2-35 to illustrate the use of structure pointers in Go. The result is shown in Listing 2-36.
package main
import "fmt"
type Books struct {
        bTitle      string
        bAuthorName string
        bSubject    string
        book_id     int
}
func main() {
       var Book1 Books /* Declaring variable of type Book */
       var Book2 Books /* Declaring variable of type Book */
       /* Access member fields of Book struct to define Book1 */
        Book1.bTitle = "The Go Programming Language"
        Book1.bAuthorName = "Alan A. A. Donovan and Brian W. Kernighan"
        Book1.bSubject = "A complete guide to Go programming"
        Book1.book_id = 6495
        /* Access member fields of Book struct to define Book2 */
        Book2.bTitle = "The Complete Book of Arts & Crafts"
        Book2.bAuthorName = "Dawn Purney"
        Book2.bSubject = "The Complete Book of Arts and Crafts of fun activities for children"
        Book2.book_id = 6496
        /* Print the details of Book1 by passing it as a pointer to function */
        printBookDetails(&Book1)
        /* Print the details of Book2 by passing it as a pointer to function */
        printBookDetails(&Book2)
}
func printBookDetails(book *Books) {
        fmt.Printf(" Title : %s ", book.bTitle)
        fmt.Printf("Authors : %s ", book.bAuthorName)
        fmt.Printf("Subject : %s ", book.bSubject)
        fmt.Printf("Book ID : %d ", book.book_id)
}
Listing 2-36

Program to Illustrate Structure Pointers in Go

/*Output:
Title   : The Go Programming Language
Authors : Alan A. A. Donovan and Brian W. Kernighan
Subject : A complete guide to Go programming
Book ID : 6495
Title   : The Complete Book of Arts & Crafts
Authors : Dawn Purney
Subject : The Complete Book of Arts and Crafts of fun activities for children
Book ID : 6496
*/

The struct type in Go is a data structure that has similar purpose and function as the struct type in C and as classes in Java. Structs can encapsulate data and methods. However, unlike Java, Go does not have an inheritance model, that is, there is no concept of a super() function or substructures. Each structure is independent with its own fields for data management and optionally its own methods. Remember that struct in Go is a custom type. Note that the structure name starts with a capital letter, e.g., Book, to ensure that it can be used in other parts of the applications, i.e., exported, similar to public access specifiers in C-style languages. On the other hand, if a lowercase initial character is used, the struct type will be private and cannot be exported for use in other parts of the application.

Program Flow

Conditional logic can be used to change the program flow. Like other C-style languages, Go also offers conditional statements like if, if…else, nested if, switch, and select. However, there is a slight syntax difference in their uses.

If Statement

For most of the part, the if statement in Go looks the same as in C or Java; however, it does not need parentheses around the Boolean condition. Another important aspect is how you format your conditional code blocks. In other C-style languages, there is the option available of starting your beginning brace of a code block on the next line. This is not the case with Go, and doing so will throw an error. The opening brace has to be on the same line as the preceding statement. Listing 2-37 is an example of using an if-else statement is Go.
package main
import (
        "fmt"
)
func main() {
        theAnswer := 42
        var result string
        if theAnswer < 0 {
                result = "Less than Zero"
        } else if theAnswer == 0 {
                result = "Equal to Zero"
        } else {
                result = "Greater than Zero"
        }
        fmt.Println(result)
}
Listing 2-37

Illustration of an if-else Statement in Go

/*Output:
Greater than Zero
*/
Another interesting and sometimes useful variation in Go’s if syntax is that you can include an initial statement that’s part of the if statement. For example, you can initialize a variable before performing conditional logic based on its value. Listing 2-38 is an example of doing this.
if x:= 50; if x < 0 {
      result = "Less than Zero"
} else if x == 0 {
      result = "Equal to Zero"
} else {
      result = "Greater than Zero"
}
fmt.Println(result)
Listing 2-38

An if Statement with Initialization

Output:
Greater than Zero

Switch Statement

Like other C-Style languages, the Go language also offers switch statements that serve the same purpose; however their syntax expands on those languages. A switch statement can be used on a variable to test a variable’s equality against a list of values. Listing 2-39 uses the Math.Rand package from the rand package; we will be using the seed() function and passing it the current time in UNIX format using the statement rand.seed(time.Now().Unix()). Next, the Intn() function from the rand package returns a non-negative pseudo-random integer. The Intn() function requires you to specify a range interval [0,n). It is important to make sure that n <= 0. In this example, we provided a ceiling value of 7+1, i.e., the function will use the interval [0, 7) + 1. In this way the function will return a number between one and seven. Each time you run this application, it will generate a different number depending on the millisecond of the current time on your computer. (It is possible to see the same number again and again, but that’s just a coincidence.) Every time the code in Listing 2-39 runs, you will possibly get different output.
package main
import (
        "fmt"
        "math/rand"
        "time"
)
func main() {
        rand.Seed(time.Now().Unix())
        dow := rand.Intn(7) + 1
        fmt.Println("Day: ", dow)
        var result string
        switch dow {
        case 1:
               result = "It's Sunday"
        case 2:
               result = "It's Monday"
        default:
               result = "It's some other day"
        }
        fmt.Println(result)
}
Listing 2-39

Using a switch Statement in Go

Output:
Day:  2
It's Monday
Note that in the switch statement, the parentheses are not required around the expression that is being evaluated. One more difference from C and Java is that the switch statement in Go does not require the break statement. In Go, as soon as one of the cases is evaluated as true, the statements within that case are executed and the control is transferred to the end of the switch statement. Furthermore, just like the if statement, you can also include a short statement before the evaluation of the variables in the switch statement, as shown in Listing 2-40. Remember that any variable declared in the switch statement will be local to that switch statement.
package main
import (
      "fmt"
      "math/rand"
      "time"
)
func main() {
      rand.Seed(time.Now().Unix())
      var result string
      switch dow := rand.Intn(7) + 1; dow {
      case 1:
            result = "It's Sunday"
      case 2:
            result = "It's Monday"
      default:
            result = "It's some other day"
      }
      fmt.Println(result)
}
Listing 2-40

Using a Switch Statement with Evaluation Statement

In Go, the fallthrough keyword is used in the case block of the switch statement. If the fallthrough keyword is present in any case block, it will transfer the program’s control flow to the next case, whether or not the current case evaluated to true.

For Statement

Unlike the other high-level programming languages, Go offers only one looping construct, namely the for loop. The for loop can be used for repetitive tasks such as iterating through collections of values. The basic syntax of the for loop is identical to that in Java or C-style languages, with the exception that the parentheses ( ) in the for statement are not required. Furthermore, just like C-style languages or Java, the pre- and post- statements in the for loop statement are not mandatory and can be left blank. Additionally, in Go, a for loop can be used in four variations, discussed here:
  1. 1.

    A for loop with step variable, condition, and range.

    The range keyword is used in Go to iterate over elements stored in a variety of data structures. When used with arrays and slices, the range keyword also returns the index and value for each entry. In Listing 2-41, in the second for loop, we used the blank identifier ( _ ) to ignore the index. However, as in the third for loop, the indexes are sometimes required as well. In the fourth for loop, the range will return to the variable i from a comma-delimited list. The first variable will be the index and the second will be the associated value.

    package main
    import "fmt"
    func main() {
        total := 0
        for k := 0; k < 5; k++ {
            total += k
        }
        nums := []int{2, 3, 4}
        total = 0
        for _, num := range nums {
            total += num
        }
        fmt.Println("Total:", total)
        for j, num := range nums {
            if num == 2 {
                fmt.Println("Index:", j)
            }
        }
        for i := range nums {
            fmt.Println(nums[i])
        }
    }
    Listing 2-41

    For Loop with Step Variable, Condition, and Range

    Output:
    Total: 9
    Index: 0
    2
    3
    4
     
  2. 2.

    A for loop with blank pre/post statements

    Listing 2-42 illustrates the use of for loops with blank pre/post statements.

    total := 1
    for ; total < 10; {
        total += total
    }
    Listing 2-42

    For Loop with Blank Pre/Post Statements

     
  3. 3.

    A for loop as a while loop

    Many languages have a while keyword that lets you loop over a set of statement(s) as long as a Boolean expression is true. Go implements this type of looping using the for keyword. Instead of a counter variable or a range, you just declare a Boolean condition. Listing 2-43 illustrates this concept.

    total := 1
    for  < 10 {
       total += total
    }
    Listing 2-43

    Using a For Loop as a While Loop

     
  4. 4.

    Infinite loop

    In general, if the specified condition statement of a loop never turns false, the loop turns into an infinite loop. Usually, for loops are used for creating infinite loops. When all of the three expressions in the for statement are not specified, this makes the condition statement remain true forever, turning the for loop into an infinite loop. Listing 2-44 illustrates this concept.

    for {
       // Any task you want to loop forever
    }
    Listing 2-44

    Using a For loop as an Inifinite Loop

     

The goto Statement

Go also supports the goto statement, which transfers control to a label in the code. Listing 2-45 illustrates the use of labels with goto statements to alter the program control flow. In this snippet, when the if statement turns true, the goto statement will be executed and control will be transferred to the theEnd label, then the statements following this label will be executed. Listing 2-45 illustrates a basic program depicting the use of a goto statement in Go.
package main
import (
        "fmt"
)
func main() {
        total := 1
        for total < 5 {
                total += total
                fmt.Println("Total: ", total)
                if total == 5 {
                       goto theEnd
                }
        }
theEnd:
        fmt.Println("End of Program")
}
Listing 2-45

Using the goto Statement in Go

Output:
Total:  2
Total:  4
Total:  8
End of Program

To summarize, Go supports the use of continue, break, and goto statements. Go also supports looping with a variety of coding patterns and adds features to the for statement to make it concise and readable.

Functions

Go is organized into packages and packages have functions. Every Go application has a package named main and at least one function, also named main. The main function is called automatically by the runtime as the application starts up. In Go, like other programming languages, you can also create custom functions and organize them into your own custom packages.

A function declaration tells the Go compiler what the function name is, its return type, and the input parameters. On the other hand, the function definition is the actual body of the function and is a set of the statement(s) that perform the intended task.

Several built-in functions are provided by the Go standard library that you can call and use in your Go programs. For example, the len() function takes arguments of various types as input and returns their length. Functions are also called methods, subroutines, or procedures.

Defining a Function

A function definition in Go is made up of two parts—the function header and the body. The general format for defining a function in Go is shown in Listing 2-46.
func function_name( [parameter list] ) [return_types]
{
   body of the function
}
Listing 2-46

General Format of Functions in Go

Here, the func keyword instructs the compiler about the start of a function declaration and function_name represents the function’s name. Function signature is referred to as the function’s name and input parameter list combined. Also, parameters are used as placeholders. Values passed to the function when it is invoked are called function arguments and these parameters are also referred to as formal parameters. A function’s parameter list defines the type, order, and total count parameters it will take as input. It is possible that a function may have no parameters and hence the parameters are optional. return_type is used to define the data type of value(s) the function may return. Here again, the return_type is optional if the function does not return anything. The body of the function contains multiple statements that together define the task it has to perform.

Listing 2-47 illustrates the use of custom functions in Go. Here, findMaximum() is a user-defined function that takes two parameters, n1 and n2. As output, it returns the maximum value among the two passed values. Also, because both the parameters are of the same type, it is not necessary to specify the type with each variable.
/* Function to find maximum value among two numbers */
func findMaximum(n1, n2 int) int {
      /* Declaring Local Variables */
      var maxVal int
      if (n1 > n2) {
        maxVal = n1
      } else {
           maxVal = n2
      }
      return maxVal
 }
Listing 2-47

Basic Program Illustrating Custom Functions in Go

Like other languages, Go functions can also accept an arbitrary number of values of the same type. To do this, as shown in Listing 2-48, you declare the parameter name, then add three dots, and then specify the data type. As values will act as an array, the range keyword can be used to loop over the values stored in it.
package main
import (
        "fmt"
)
func main() {
        total := addValues(40, 50, 60)
        fmt.Println("Sum of Passed Values: ", total)
}
func addValues(values ... int) int{
        sum := 0
        for _, val := range values{
                sum += val
        }
        return sum
}
Listing 2-48

Functions with Arbitrary Number of Values

/*Output:
Sum of Passed Values:  150
*/

Doing Function Calls in Go

In order to create a function in Go, it is necessary to provide its definition as well, which defines what task the function should perform. In order to use a function, it has to be called from the program. When a function call is made from a program, the control flow is transferred to it. When the called function finishes the defined task or a return statement is encountered or the closing brace of the function is reached, the control flow returns to the program where the initial call was made.

To make a function call, you simply pass the required parameters, wrapped within parentheses () next to the function name. If a value is to be returned from the function, the returned values can either be used directly, like with Println(), or can be stored and reused later for performing different operations. Listing 2-49 illustrates the concept of performing function calls in Go.
package main
import "fmt"
func main() {
   /* Defining Local Variables */
   var val1 int = 50
   var val2 int = 80
   var retVal int
   /* Function call to findMaximum() */
   retVal = findMaximum(val1, val2)
   fmt.Printf("Maximum value: %d ", retVal)
}
/* Function to find maximum value between the two numbers */
func findMaximum(num1, num2 int) int {
   /* Declaring Local Variables */
   var maxVal int
   if (num1 > num2) {
      maxVal = num1
   } else {
      maxVal = num2
   }
   return maxVal
}
Listing 2-49

Basic Program to Illustrate Function Calls in Go

Output:
Maximum Value: 80

Return More than One Value from Functions

Unlike other C-style languages, Go functions are designed to return multiple values at a time. The following example illustrates how to return values from a function. Refer to the official documentation at https://golangdocs.com/functions-in-golang to learn more ways of returning values and syntax variations in Go. Listing 2-50 illustrates this concept.
package main
import (
   "fmt"
)
func swapValues(str1, str2 string) (string, string) {
   return str2, str1
}
func main() {
   val1, val2 := swapValues("Abdullah", "Hassan")
   fmt.Println("Values After Swap: ", val1, val2)
}
Listing 2-50

Go Program Illustrating How to Return More than One Value from Functions

Output:
"Values After Swap: Hassan Abdullah

Passing Arguments to Functions

You must declare all of the variables that would accept the values passed to the function as its arguments. This set of variables are also known as a function’s formal parameters. Note that the scope of all of the formal parameters of a function is local to it only, i.e., they are accessible only within the scope of that particular function. Furthermore, the formal parameters are only created upon entering the function and are destroyed when it exits.

There are two ways to pass arguments to a function—call by value and call by reference. When the call by value way of passing arguments is used, it basically copies the value of the passed argument into the formal parameters of the called function. Any changes made to the formal parameters inside the respective function have no effect on the value of the argument that was passed with function call.

On the other hand, the call by reference technique copies the direct address of the passed argument into the formal parameters of the called function. As the direct address is copied and used to access the actual argument, any changes made to the formal parameters inside the respective function will also be reflected in the argument passed with the function call. To ensure that a function cannot change the value of the passed arguments, by default, call by value is used to pass arguments to functions in the Go programming language.

Call by Value Example

Listing 2-51 illustrates the concept of call by value in Go.
package main
import (
      "fmt"
)
func modifyInt(val int) int {
      return val + 5
}
func main() {
      num := 500
      fmt.Println("Value of Passed Argument
                  Before Function Call: ", num)
      fmt.Println("Value Returned by Function call:",
                  modifyInt(num))
      fmt.Println("Value of Passed Argument
                  After Function Call: ", num)
}
Listing 2-51

Go Program Showing Call by Value

Output:
Value of Passed Argument Before Function Call:  500
Value Returned by Function call: 505
Value of Passed Argument After Function Call:  500

Call by Reference Example

Listing 2-52 illustrates the concept of call by reference in Go.
package main
import (
        "fmt"
)
func myMap(mapObject map[int]int) {
        //make() declares as well as initializes the map to 0
        mapObject = make(map[int]int)
}
func myInt(values []int) {
        //make() declares as well as initializes the slice to 0
        values = make([]int, 5)
}
func main() {
        //mapObject is declared but NOT initialized, i.e., its value is nil
        var mapObject map[int]int
        myMap(mapObject)
        fmt.Println("Is the map equal to nil? ", mapObject == nil)
        //Slice is declared but NOT initialized, i.e., its value is nil
        var intSlice []int
        myInt(intSlice)
        fmt.Println("Is the slice equal to nil? ", intSlice == nil)
}
Listing 2-52

Go Program Illustrating Call by Reference in Go

Output:
Is the map equal to nil?  true
Is the slice equal to nil?  true

Methods

In Go, if functions are attached to user-defined custom types, they are then referred to as methods. In the case of an object-oriented language like Java, each method is a member of a class. Whereas, in Go a method is a member of a type. Listing 2-53 illustrates how to attach a method to the struct.
package main
import (
        "fmt"
)
func main() {
        poodle := Dog{"Poodle", 10, "woff!"}
        fmt.Println(poodle)
        fmt.Printf("%+v ", poodle)
        fmt.Printf("Breed: %v Weight: %v ", poodle.Breed, poodle.Weight)
        poodle.Speak()
}
//Dog is a struct
type Dog struct {
        Breed  string
        Weight int
        Sound  string
}
func (d Dog) Speak() {
         fmt.Println(d.Sound)
}
Listing 2-53

Example Illustrating the Use of Methods in Go

/*Output:
{Poodle 10 woff!}
{Breed: Poodle Weight:10 Sound:woff!}
Breed: Poodle
Weight: 10
woff!
*/
Go doesn’t support method overriding; each method must have its own unique name. But like all functions, methods can return values just to clear the type assigned to the method, as shown in Listing 2-54.
package main
import (
        "fmt"
)
func main() {
        poodle := Dog{"Poodle", 10, "woff!"}
        fmt.Println(poodle)
        fmt.Printf("%+v ", poodle)
        fmt.Printf("Breed: %v Weight: %v ", poodle.Breed, poodle.Weight)
        poodle.Speak()
        poodle.Sound = "Arf!"
        poodle.Speak()
        poodle.SpeakThreeTimes()
}
//Dog is a struct
type Dog struct {
        Breed  string
        Weight int
        Sound  string
}
func (d Dog) Speak() {
        fmt.Println(d.Sound)
}
func (d Dog) SpeakThreeTimes() {
        d.Sound = fmt.Sprintf("%v %v %v", d.Sound, d.Sound, d.Sound)
        fmt.Println(d.Sound)
}
Listing 2-54

Methods with Returning Values

/*Output:
{Poodle 10 woff!}
{Breed:Poodle Weight:10 Sound:woff!}
Breed: Poodle
Weight: 10
Woff!
Arf!
Arf! Arf! Arf!
*/

Note that when you pass the dog object to a function as the receiver, a copy is made of it, i.e. this is pass by value and not reference. If you want it to be a reference, you can use pointers. But within the function, this is a brand new copy of the object. So when you modify the sound field, you are not modifying the version that was created in the main function. The ability to create custom methods for your own types makes the Go programming language behave more like a fully object-oriented language. Even without the sort of type inheritance that you find in C++ and Java, a struct can have as many different methods as it needs to accomplish your application’s requirements.

Write/Read Text Files

There are a number of ways to create a file in Go. There are high-level abstractions of the low-level operations that are required.

Write Text Files

The example in Listing 2-55 shows you how to write to a file in Go. The checkError() will be used for error checking throughout this program. The function will check the object passed to it and if the object is not nil, i.e., an error has occurred performing some operation in the main program, the function will crash the application and display the error message.

In the main() function, the content variable holds the string value you want to write to the fromString.txt file. On Line#2, in the main() function, we create a file reference named file, which is basically a variable that will be used as a reference to the file. Along with this, we also specify a variable called err to store error messages returned by the os.Create() function. The Create() function takes the location of the file to which the data should be written. Furthermore, it creates the named file if it doesn’t exist or truncates it if it already exists. Here, the ./ means the file is in the same directory as the application.

On Line#4 in the main() function, the length variable stores the number of bytes written to the file as returned by the WriteString() function. The WriteString() function requires a writer object and a string, here we passed file as the writer and the content variable as the string that has to be written to the file. Whenever you’re working with files, it is of utmost importance to close files that you opened when you are done. The defer keyword is waiting until everything else is done and then executes this command. With the defer keyword, we have written the file.Close() command to ensure that when everything is done, the file referred to in the file reference object is closed.
package main
import (
        "fmt"
        "io"
        "io/ioutil"
)
func main() {
        content := "Hello from Go! "
        file, err := os.Create("./fromString.txt")
        checkError(err)
        defer file.Close()
        length, err := io.WriteString(file, content)
        checkError(err)
        fmt.Printf("Wrote a file with %v characters ",length)
}
/* checkError is a function for error checking */
func checkError(err error) {
        if err != nil {
                panic(err)
        }
}
Listing 2-55

Program Illustrating How to Write to Text Files in Go

Output:
Wrote a file with 14 characters

A screenshot of a program output reads Hello from Go.

Read Text Files

In this section, in order to illustrate how reading data from a text file can be achieved in Go, as shown in Listing 2-56, we modify the previous example in Listing 2-55 by adding a custom function called readFile() which will take a filename as input. Whenever a file is read, it is always returned as an array of bytes. The data variable in Listing 2-56 will hold the returned bytes. Once again, the err object checks if an error is thrown. The ReadFile() function is a built-in function in the ioutil package that can be used to read any file indicated by the filename passed to it as an argument. As output, it returns the contents of the file read. Upon successful execution of the ReadFile(), the error value is returned as nil instead of EOF. This is because the ReadFile() function reads as input the entire file and does not treat an EOF returned from the Read() function as an error. At the last line in the readFile(), the string(data) statement is used to type-cast the contents of the file received as bytes into the string to display them on the screen. In the main() function we used the defer keyword again and then called the readFile() function and passed it the file we want to read. It is important to use the defer keyword whenever you are working with anything that might not run automatically in the current thread. Also, if you want to wait until the file is completely closed before you try to read it, the defer keyword achieves this for you.
package main
import (
        "fmt"
        "io"
        "io/ioutil"
        "os"
)
func main() {
        content := "Hello from Go!"
        file, err := os.Create("./fromString.txt")
        checkError(err)
        length, err := io.WriteString(file, content)
        checkError(err)
        fmt.Printf("Wrote a file with %v characters ", length)
        readFile(file.Name())
        defer file.Close()
}
// readFile is a function for reading content of text file
func readFile(fileName string) {
        data, err := ioutil.ReadFile(fileName)
        checkError(err)
        fmt.Println("Text read from file: ", string(data))
}
// checkError is a function for error checking
func checkError(err error) {
         if err != nil {
                 panic(err)
         }
}
Listing 2-56

Reading from Text Files in Go

Output:
Wrote a file with 14 characters
Hello from Go!

HTTP Package

To help programmers build applications that can effectively communicate with different web applications and services, the Go programming language offers an extensive set of different tools, one of them being the http package. The http package allows you to easily communicate with remote hosts by creating requests and sending data. It also helps in creating HTTP server applications that can listen and respond to the received requests. The example in Listing 2-57 illustrates the use of the http package.

In Listing 2-57, a request is sent to a remote host to fetch some data. Here, we want to download the JSON contents of the page specified in the content named url to the local development machine. To do so, the first step is to import the net/http and ioutil packages. To download the content use the get() function from the http package. The get() function returns a response object as well as an error object. The resp variable is used to store the returned response object. When you print the contents of the resp variable, you will get a pointer to an object named response. Notice that it’s a member of the http package. The response object has a public field called body. It contains the JSON packet you intend to download. Just like with files, after reading the contents of the body, you should use the defer keyword along with the resp.Body.Close() command to close the body.

The contents are received in the form of a byte array. To store this, use the bytes variable. The ioutil.ReadAll(resp.Body) reads the contents of the resp.Body. Since the returned content is a byte array, you have to type-cast it into a string to print it onto the screen, by wrapping the bytes variable within the string type using the string(bytes) command.
package main
import (
        "fmt"
        "io/ioutil"
        "net/http"
)
const url = "http://services.explorecalifornia.org/json/tours.php"
func main() {
        fmt.Println("Network Requests Demo")
        response, err := http.Get(url)
        if err != nil {
                panic(err)
        }
        fmt.Printf("Response Type: %T ", response)
        defer response.Body.Close()
        bytes, err := ioutil.ReadAll(response.Body)
        if err != nil {
                panic(err)
        }
        content := string(bytes)
        fmt.Print(content)
}
Listing 2-57

Basic Program Showing the Use of the HTTP Package in Go

Output:

A screenshot of the output of the program in Go.

JSON

The data returned as a response to a request made to a web service is commonly encoded in the JSON format. To facilitate working with web services and APIs, Go offers support for handling JSON-formatted data in the form of the json package. The json package allows you to easily create and read text that is in the JSON format. In the previous section, you learned how to get JSON data from a web service. In this section, you learn how to parse the JSON data and format it into structured data for use in your Go applications. Listing 2-58 is a demonstration of how to parse JSON data and format it into structured data. It parses the tour name and price from the received JSON data.

In Listing 2-58, you first create a new custom type named Tour, which will be a struct with two member fields of string type—Name and Price. Note that to make these field names public, they start with an uppercase character. Also note that in the JSON content the labels are all in lowercase. However, the JSON decoder is not affected by this, as it can easily match these labels to the members of the struct. For this example, we imported the encoding/json (https://pkg.go.dev/encoding/json), io/ioutil (https://pkg.go.dev/io/ioutil), net/http (https://pkg.go.dev/net/http) and strings (https://pkg.go.dev/strings) packages.

The toursFromJson() function is a custom function used to decode the body content in the JSON format of the HTTP response. It takes one argument, which is the JSON-formatted string fetched from the website. The function will return these values as structured data, specifically a slice containing instances of that tour object.

Within the toursFromJson() function, the first step is to create a slice tour of tour objects. The tour slice is initially set to size zero and an initial capacity of 20. Note that 20 is just a guess and not the exact number of objects that might be fetched. As slices can resize dynamically, you don’t have to give a large initial capacity. The NewDecoder() built-in function returns a decoder that will read from the reader object passed to it. The NewReader() returns a reader that reads from the string passed to it. The Token() function returns the next JSON token in the input stream.

To transform the JSON-formatted text into the slice of tour objects, you declare a variable named tours and set its type to Tour. Next, a while-style for loop is used; the decoder.More() function reports whether or not any other elements are present in the current array or any other object is getting parsed. Within the for loop, the Decode() function of the decoder object reads the next JSON-encoded value from its input and stores it in the value pointed to by the argument passed to it. You pass the memory address of the tour object. Then you add the tour object to the tours slice using the append() function. In the end, the tours object is returned from the function.
package main
import (
        "encoding/json"
        "fmt"
        "io/ioutil"
        "net/http"
        "strings"
)
const url = "http://services.explorecalifornia.org/json/tours.php"
func main() {
        resp, err := http.Get(url)
        if err != nil {
                panic(err)
        }
        fmt.Printf("Response Type: %T ", resp)
        defer resp.Body.Close()
        bytes, err := ioutil.ReadAll(resp.Body)
        if err != nil {
                panic(err)
        }
        content := string(bytes)
        tours := toursFromJson(content)
        for _, tour := range tours {
                fmt.Println(tour.Name, "  ", tour.Price)
       }
}
func toursFromJson(content string) []Tour {
        tours := make([]Tour, 0, 20) //slice of Tour array with initial size 0 and capacity 20
        decoder := json.NewDecoder(strings.NewReader(content))
        _, err := decoder.Token()
        if err != nil {
               panic(err)
        }
        var tour Tour
        for decoder.More() {
                err := decoder.Decode(&tour)
                if err != nil {
                        panic(err)
                }
                tours = append(tours, tour)
        }
        return tours
}
type Tour struct {
        Name, Price string
}
Listing 2-58

Basic Program Illustrating How to Handle JSON Data in Go

/*Output:
2 Days Adrift the Salton Sea    350
A Week of Wine    850
Amgen Tour of California Special    6000
Avila Beach Hot springs    1000
Big Sur Retreat    750
Channel Islands Excursion    150
Coastal Experience    1500
Cycle California: My Way    1200
Day Spa Package    550
Endangered Species Expedition    600
Fossil Tour    500
Hot Salsa Tour    400
Huntington Library and Pasadena Retreat Tour    225
In the Steps of John Muir    600
Joshua Tree: Best of the West Tour    150
Kids L.A. Tour    200
Mammoth Mountain Adventure    800
Matilija Hot springs    1000
Mojave to Malibu    200
Monterey to Santa Barbara Tour    2500
Mountain High Lift-off    800
Olive Garden Tour    75
Oranges & Apples Tour    350
Restoration Package    900
The Death Valley Survivor's Trek    250
The Mt. Whitney Climbers Tour    650
*/

Summary

This chapter covered the basic programming fundamentals you need to get started programming in Go, ranging from how to install the Go compiler and different IDEs available, the program structure of Go, to programming concepts. You also gained insight into how to use different data structures and programming features like variables, taking user input, available math operators and packages, managing memory and referencing values, and using pointers. You learned how to manage ordered values using arrays and slices, how to use maps to store key-value pairs, and how to define user-defined types using structs.

Managing program flow is a key point in programming, and this chapter also provided guidance into how to control the flow using different conditional statements, like if, if..else, switch, and goto. You also learned about the only type of loop available in Go that can be used in whichever way possible to achieve desired results.

Functions are an important part of modular programming. This chapter also covered how to use user-defined functions and attach methods to slices in Go. Another important capability of any program is the ability to read and write to files. This chapter also provided details into doing this. It also covered how to use HTTP package and handle JSON data. This is important to help programmers build applications that can effectively communicate with different web applications and services.

In the upcoming chapters, you’ll go through Go recipes based on real-life scenarios in order to get a better understanding of the concepts covered in this chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.148.124