GoLang The Ultimate Guide
GoLang The Ultimate Guide
GoLang is becoming one of the most popular languages, which means that
learning it can open up new doors of opportunity and even help you land a
job at various companies that use Go extensively.
Edited by
Sufyan bin Uzayr
First edition published 2023
by CRC Press
6000 Broken Sound Parkway NW, Suite 300, Boca Raton, FL 33487-2742
Reasonable efforts have been made to publish reliable data and information, but the author and
publisher cannot assume responsibility for the validity of all materials or the consequences of their
use. The authors and publishers have attempted to trace the copyright holders of all material
reproduced in this publication and apologize to copyright holders if permission to publish in this
form has not been obtained. If any copyright material has not been acknowledged please write and let
us know so we may rectify in any future reprint.
Except as permitted under U.S. Copyright Law, no part of this book may be reprinted, reproduced,
transmitted, or utilized in any form by any electronic, mechanical, or other means, now known or
hereafter invented, including photocopying, microfilming, and recording, or in any information
storage or retrieval system, without written permission from the publishers.
For permission to photocopy or use material electronically from this work, access
www.copyright.com or contact the Copyright Clearance Center, Inc. (CCC), 222 Rosewood Drive,
Danvers, MA 01923, 978-750-8400. For works that are not available on CCC please contact
[email protected]
Trademark Notice: Product or corporate names may be trademarks or registered trademarks and are
used only for identification and explanation without intent to infringe.
Typeset in Minion
by KnowledgeWorks Global Ltd.
Contents
Acknowledgments
APPRAISAL
CHEAT SHEET
BIBLIOGRAPHY
INDEX
Acknowledgments
There are many people who deserve to be on this page, for this book would
not have come into existence without their support. That said, some names
deserve a special mention, and I am genuinely grateful to:
Sufyan bin Uzayr is a writer, coder, and entrepreneur with over a decade of
experience in the industry. He has authored several books in the past,
pertaining to a diverse range of topics, ranging from History to
Computers/IT.
Sufyan is the Director of Parakozm, a multinational IT company
specializing in EdTech solutions. He also runs Zeba Academy, an online
learning and teaching vertical with a focus on STEM fields.
Sufyan specializes in a wide variety of technologies, such as JavaScript,
Dart, WordPress, Drupal, Linux, and Python. He holds multiple degrees,
including ones in Management, IT, Literature, and Political Science.
Sufyan is a digital nomad, dividing his time between four countries. He
has lived and taught in universities and educational institutions around the
globe. Sufyan takes a keen interest in technology, politics, literature,
history, and sports, and in his spare time, he enjoys teaching coding and
English to young students.
Learn more at sufyanism.com.
chapter 1
Crash Course in GoLang
DOI: 10.1201/9781003309055-1
IN THIS CHAPTER
➢
What is GoLang?
➢
Major concepts
➢
Advantages and disadvantages
➢
Syntax and code basics
➢
Additional info
IS IT REFERRED TO AS Go OR GoLang?
Is the language known as Go or GoLang? Google takes it from the horse’s
mouth: the language is called “Go.”
According to Google, the name “GoLang” originates from the URL
GoLang.org, which was chosen since “go.org was not available to us.” As a
result, some individuals use the terms “GoLang” and “Go” interchangeably
when referring to the programming language.
To be fair, GoLang is far more Google-able than Go, which may refer to
either the strategic game or the cult-classic Doug Liman film. GoLang is
also the Twitter hashtag for Go, #GoLang.
Python vs. Go
Python has been for more than 30 years, yet its popularity continues to rise.
The language’s design has withstood the test of time (despite some rough
spots along the way). Python and Go are two of the most popular and user-
friendly programming languages available today.
Python is an excellent object-oriented programming language, but it is
also possible to develop programs with a functional programming approach.
Python language is perhaps the most widely used programming language
among non-programmers. Python’s versatility is one of the reasons it is so
popular. Use for anything from cleaning out files on your computer to
developing online apps, serverless projects, teaching programming to
children, working on animation, etc.
Go PROGRAMMING FEATURES
Unmatched Simplicity of Go
One of the key advantages of using Go is its simplicity. Despite being a
very advanced language with a robust feature set, Go stands out from the
crowd owing to its ease of use and fundamental approach.
BEGINNING WITH Go
Numerous online IDEs, like The Go Playground, repl.it, and many others,
can run Go applications without requiring any installation.
We need the following two pieces of software to install Go on our PCs or
laptops: Text editor and Compiler.
Text Editor
We can write our source code on a text editor’s platform. Below are some
text editors:
Brief
OS Edit command
Epsilon
Windows notepad
VS Code
Emacs
vm or vi
Finding a Go Compiler
The Go distribution is provided as a binary installer for FreeBSD, Mac OS
X, Linux, and Windows with 32-bit (386) and 64-bit (amd64) x86 CPU
architectures.
INSTALL Go ON WINDOWS
We must first install GoLang on our machine before we can proceed. We
need a personal understanding of the Go Language and what it can achieve.
Go is an open-source, statically typed language developed by Google’s
Robert Griesemer, Rob Pike, and Ken Thompson in 2007 but only
published in 2009. It is also known as GoLang, and it supports the
procedural programming language. Initially, it intended to increase
programming efficiency on big codebases, multicore, and networked
devices.
GoLang programs are simple to create. They may be written in any plain
text editor, such as notepad++ or anything similar. To make creating and
working on GoLang code easier, one can utilize an online IDE or install one
on their machine. The best part is that the IDE makes writing GoLang code
easier because IDEs have numerous capabilities such as an intuitive code
editor, debugger, compiler, etc.
First, install the Go Language on the system to develop GoLang Codes
and conduct different exciting and beneficial tasks.
If GoLang has been installed on our PC, it will create a message with all the
GoLang version’s data; otherwise, an error reading “Bad command or file
name” will occur.
Step 1: After we’ve downloaded the archive file, unzip it. We’ll
discover a go folder in our current working directory after unzipping.
Step 2: Copy/paste the extracted folder to anywhere we want it to go.
In this situation, we’ll install it on the C drive.
Step 3: Configure the environment variables now. Right-click My
Computer and choose Properties. Select Advanced System Settings
from left menu, followed by Environment Variables.
Step 4: Choose Path and then Modify from the system variables. Then,
choose New and type in the Path with bin directory where we placed
the Go folder. We’ll update the path to C:gobiC:\go\bin and click OK.
Step 5: Make a new user variable that informs the Go command where
to find the GoLang libraries. To do so, navigate to User Variables and
click New.
For the Variable name, enter GOROOT, and for the Variable value, enter the
path to our GoLang folder. As a result, the Variable value in this example is
C:\go\. Click OK once we’ve completed filling out the form.
Then, under Environment Variables, click OK, and our configuration is
finished. Now, use the command prompt to check the GoLang version by
entering the Go version.
Following the download, any text editor or IDE may use to write GoLang
Codes, which can launch on the IDE or the Command prompt with the
following command:
go run filename.go
Line 1: It comprises the core package of the software and its general
content.
It is the program’s beginning point. Hence, it must be written.
Line 2: It includes the preprocessor instruction import “fmt,” which
informs the compiler to include the files in the package.
Line 3: It is the main function; this is where the program’s execution
begins.
Line 4: It is fmt.
SingleLine comment
Syntax:
// singleline-comment
Multiline comment
Syntax:
Example:
package main
import "fmt"
func main() {
fmt.Println("3 + 3 =", 3 + 3)
}
The preceding program’s explanation: The following program uses
the identical package line, import line, function declaration, and
Println function as the prior GO program. We print 3 + 3 = followed by
the result of equation 3 + 3 instead of the text “Hello, everyone.” This
expression comprises three parts: int numeric literal 3, + operator
(which indicates addition), and another int numeric literal 3.
Hardware Restrictions
Over a decade, we’ve seen hardware and processor configurations change
rapidly. The P4 had a clock speed of 3.0 GHz in 2004. The Macbook Pro
has a clock speed of about in 2018. (2.3 GHz vs. 2.66 GHz). Additional
processors are used to speed up functionality. However, the expense of
utilizing more processors grows as well. Consequently, we employ fewer
processors, and with fewer processors, we have a heavy programming
language whose threading uses more memory and slows down the speed of
our system.
To resolve this challenge, GoLang was intended to utilize goroutine
instead of threading, which is comparable to threading but consumes
considerably less memory. Because threading requires 1 MB of memory
and goroutines consume 2 KB, it is simple to activate millions of goroutines
simultaneously. As a result of the above arguments, GoLang is a powerful
language that manages concurrency in the same manner as C++ and Java.
Downsides:
TERMINAL
GoLang has a terminal emulator, allowing us to communicate with our
command-line shell within the IDE. It can run Git commands, change file
permissions, and do other command-line tasks without using specific
terminal software.
The terminal emulator starts with our default system shell, but it also
supports Windows PowerShell, Command Prompt cmd.exe, sh, bash, zsh,
csh, and other shells.
INSTALL Go ON Mac
We should first install GoLang on our machine before we proceed. We need
a personal understanding of the Go Language and what it can achieve. Go is
an open-source, statically typed language developed by Google’s Robert
Griesemer, Rob Pike, and Ken Thompson in 2007, but only published in
2009. It is also known as GoLang, and it enables the procedural computer
program. It was created to increase programming efficiency on huge
codebases, multicore, and networked devices.
GoLang applications are written with any plain text editor, including
TextEdit, Sublime Text, etc. To make creating and working on GoLang
code easier, one can utilize an online IDE or install one on their machine.
Using an IDE for simplicity makes writing GoLang code easier since IDEs
provide various capabilities such as an intuitive code editor, debugger,
compiler, etc.
The procedures for installing GoLang on MacOS are as follows:
Step 3: To verify that our .bash profile has the following path, run the
following command:
cat .bash_profile
Implement a Go Program
Let’s save the source code to a file, compile it, and run the program. Please
follow the following steps:
Hello-Everyone
Check that the Go compiler is in our path but is executing in the directory
containing the source file heyy.go.
Syntax:
func Create(filename string) (*File, error)
Example:
package main
import (
"log"
"os"
)
func main() {
// creation of an empty file
// Using the Create() method
myfile, es := os.Create("heyy.txt")
if es != nil {
log.Fatal(es)
}
log.Println(myfile)
myfile.Close()
}
Syntax:
Example:
package main
import (
"log"
"os"
)
var (
myfile *os.FileInfo
es error
)
func main() {
// The Stat() method returns file information,
and if there is no file, it gives error.
myfile, es := os.Stat("heyy.txt")
if es != nil {
// Using the IsNotExist() method to determine
whether a given file exists or not
if os.IsNotExist(es) {
log.Fatal("File was not found")
}
}
log.Println("File-Exist")
log.Println("File-Details:")
log.Println("The Name is: ", myfile.Name())
log.Println("The Size is: ", myfile.Size())
}
package main
import (
"log"
"os"
)
func main() {
if er := os.Mkdir("a", os.ModePerm); er != nil {
log.Fatal(er)
}
}
Make a directory hierarchy:
package main
import (
"log"
"os"
)
func main() {
if er := os.MkdirAll("a/b/c/d", os.ModePerm); er !=
nil {
log.Fatal(er)
}
}
The os.Mkdir() method creates a new directory with the given name but
does not enable subdirectories to be created.
Example: Use the offline compiler for the optimal outcomes. Save the
file as an a.go extension. To run the application, execute the command
listed below.
go run file-name.go
// program for reading and writing files
package main
// importing-packages
import (
"fmt"
"io/ioutil"
"log"
"os"
)
func CreateFile() {
// The fmt package provides formatted I/O and
includes functions such as Printf and Scanf
fmt.Printf("Writing file in the Go lang\n")
// If an error is thrown, it is received by the
err variable, and the Fatalf method of the
// log outputs the error message and terminates
program execution.
file, er := os.Create("test.txt")
if er != nil {
log.Fatalf("failed the creating file: %s",
er)
}
// Defer is used for cleaning reasons, such as
terminating a running file after the file has been
written, and the main function has finished
execution.
defer file.Close()
// The len variable stores the length of the
string written to the file.
len, er := file.WriteString("Welcome Everybody"+
"Program displays reading and writing"+
" operations to file in the
GoLang.")
if er != nil {
log.Fatalf("failed writing to the file: %s",
er)
}
// The Name() function returns the name of the
file that was sent to the Create() method.
fmt.Printf("\nFile-Name: %s", file.Name())
fmt.Printf("\nLength is: %d bytes", len)
}
func ReadFile() {
fmt.Printf("\n\nReading file in the GoLang\n")
fileName := "test1.txt"
// The ioutil package has built-in
// methods such as ReadFile, which reads a filename
and returns its contents.
data, er := ioutil.ReadFile("test1.txt")
if er != nil {
log.Panicf("failed reading the data from
file: %s", er)
}
fmt.Printf("\nThe File Name is: %s", fileName)
fmt.Printf("\nThe Size is: %d bytes", len(data))
fmt.Printf("\nThe Data is: %s", data)
}
// the main function
func main() {
CreateFile()
ReadFile()
}
Syntax:
func Rename(oldpath, newpath string) error
Example:
// A program that demonstrates how to rename and
delete files in a new directory.
package main
import (
"log"
"os"
)
func main() {
// File Rename and Remove
// Using the Rename() method
OriginalPath :=
"/Users/anki/Documents/new_folder/heyy.txt"
NewPath :=
"/Users/anki/Documents/new_folder/myfolder/xyz.txt"
es := os.Rename(OriginalPath, NewPath)
if es != nil {
log.Fatal(es)
}
}
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
// os.Open() open the specific file in
// the read-only mode,
// this return-pointer of type os.
file, er := os.Open("samples.txt")
if er != nil {
log.Fatalf("it failed to open")
}
//bufio. NewScanner()' function is called, and an
object os.File is passed as a parameter.
//This function returns an object bufio.Scanner which
is utilized on the
//bufio.Scanner.Split() method.
scanner := bufio.NewScanner(file)
// bufio.ScanLines is used as
// the input to method bufio.Scanner.Split()
// then scanning forwards to each new line
// using bufio.Scanner.Scan() method.
scanner.Split(bufio.ScanLines)
var text []string
for scanner.Scan() {
text = append(text, scanner.Text())
}
// method os.File.Close() is called
// on the os.File object to close
file.Close()
// then a loop iterates through,
// the prints each of slice values.
for _, each_ln := range text {
fmt.Println(each_ln)
}
}
BASIC SYNTAX
Tokens
A Go program is made up of many tokens. Keywords, identifiers, constants,
string literals, and symbols are examples of tokens. To illustrate, the
preceding Go expression is composed of six tokens:
fmt.Println("Hey, Everyone")
fmt
.
Println
(
"Hey, Everyone"
)
Line Separator
The line separator key serves as a statement terminator in a Go program. In
all the other languages, individual expressions do not necessitate using a
specific separator, such as “;” in C. The Go compiler uses the statement
terminator “;” to indicate the conclusion of one logical entity.
Consider the following statements, for example:
fmt.Println("Hey, Everyone")
fmt.Println("We are now engaged in the environment of
Go programming")
Comments
Comments are comparable to help messages in our Go program, which the
compiler ignores. They commence with/* and end with the characters */as
demonstrated below:
/* Our first Go program */
There may be no comments inside comments, nor can they be inside strings
or literal characters.
Identifiers
A Go identifier identifies a variable, function, or another user-defined item.
An identifier begins with a letter from A to Z, a to z, or an underscore.
Underscores can accompany it, zero or more characters, or numerals.
identifier = letter { letter | unicode_digit }
Keywords
The following are the reserved words in Go. These reserved words may not
be used as names for constants, variables, or other identifiers.
They are grouped into six categories: const, func, import, package, type,
and var are used in Go programs to declare distinct sorts of code
components.
Components of certain composite type denotations include chan,
interface, map, and struct.
Break, case, continue, default, else, fallthrough, for, goto, if, range,
return, select, and switch govern code flow.
Flow terms are controlled by both defer and go, but distinct ways.
Whitespace
Blanks, tabs, newline characters, and comments are examples of whitespace
in Go. A blank line contains only whitespace, maybe with a remark, and is
entirely disregarded by the Go compiler.
Whitespaces separate one segment of a statement from the next, allowing
the compiler to discern where one element, int, finishes and the next
element, int, begins in a statement. As a consequence, the given statement is
correct:
var ages int;
DATA TYPES IN Go
Data types specify the data types stored in a valid Go variable. The Go
Language divides types into various categories, which are as follows:
Numbers
Booleans
Strings
Numbers
In Go, numbers are divided into three subcategories, which are as follows:
Data
Type Description
int8 8-bit signed integer
int16 16-bit signed integer
int32 32-bit signed integer
int64 64-bit signed integer
uint8 8-bit unsigned integer
uint16 16-bit unsigned integer
uint32 32-bit unsigned integer
uint64 64-bit unsigned integer
Int In and uint have the same size, either 32 or 64 bits.
Uint In and uint have the same size, either 32 or 64 bits.
Rune It is the same as int32 and represents Unicode code points.
Byte It is an abbreviation for uint8.
It is an unsigned integer type. It does not have a defined
Uintptr
width, but it can hold all of the bits of a pointer value.
Example:
Example:
Example:
Booleans
The boolean data type only represents two values: true and false. Boolean
values are neither inherently nor explicitly changed to any other type.
Example:
Strings
A string data type comprises a sequence of Unicode code points. In other
words, a string is a collection of immutable bytes, which means that we
cannot change it once it is produced. A string can contain any human-
readable data, including zero-value bytes.
Example:
VARIABLES IN Go
A typical program uses several variables that might change during
execution. Consider a code that performs different operations on the data
user input. The values entered by one user may differ from those of another.
As an outcome, variables are necessary. This is so another user may not use
the same settings again.
When a person begins a new value in the operation process, which will
be used later, they can temporarily store it in the computer’s Random
Access Memory. The values in this part of memory change throughout the
execution, giving birth to another term for this, Variables. A variable is a
placeholder for information that may change during runtime. Variables
allow us to retrieve and manipulate stored data.
Variable naming guidelines:
Declaring a Variable
In the Go computer language, we may declare variables in two ways:
Syntax:
Example:
If the expression is removed, the variable will have a value of zero for
the type, like zero for integers, false for Booleans, "" for strings, and
nil for interface and reference types. As a result, the Go scripting
language has no concept of an uninitialized variable.
Example:
Example:
Example:
Syntax:
variable-name:= expression
Important notes:
The type of the expression determines the type of the variable in the
previous expression.
Example:
// Program to show the
// concept of variable
package main
import "fmt"
func main()
{
// Using the short-variable declaration
my_var1 := 39
my_var2 := "PiiksofPiiks"
my_var3 := 32.63
// Display the value and type of the variables
fmt.Printf("Value of my_var1 is : %d\n", my_var1)
fmt.Printf("Type of my_var1 is : %T\n", my_var1)
fmt.Printf("\nValue of my_var2 is : %s\n", my_var2)
fmt.Printf("Type of my_var2 is : %T\n", my_var2)
fmt.Printf("\nValue of my_var3 is : %f\n", my_var3)
fmt.Printf("Type of my_var3 is : %T\n", my_var3)
}
Example:
// Program to illustrate
// the concept of variable
package main
import "fmt"
func main()
{
// Using the short variable declaration
// Multiple variables of same types
// are declared & initialized in the single line
my_var1, my_var2, my_var3 := 630, 34, 76
// Display the value and
// type of the variables
fmt.Printf("The Value of my_var1 is : %d\n",
my_var1)
fmt.Printf("The Type of my_var1 is : %T\n", my_var1)
fmt.Printf("\nThe Value of my_var2 is : %d\n",
my_var2)
fmt.Printf("The Type of my_var2 is : %T\n", my_var2)
fmt.Printf("\nThe Value of my_var3 is : %d\n",
my_var3)
fmt.Printf("The Type of my_var3 is : %T\n", my_var3)
}
// Program to show
// concept of the variable
package main
import "fmt"
func main() {
// Using short variable declaration
// short variable declaration acts as
// an assignment for the my_var2 variable
// because the same variable present in same block
// so the value of my_var2 is changed from 56 to 100
my_var1, my_var2 := 39, 56
my_var3, my_var2 := 56, 100
// If we try to run the commented lines,
// the compiler will throw an error since
// these variables have already been defined.
// my_var1, my_var2 := 52, 56
// my_var2:= 230
// Display values of the variables
fmt.Printf("Value of my_var1 and my_var2 is : %d
%d\n",
my_var1,
my_var2)
fmt.Printf("Value of my_var3 and my_var2 is : %d
%d\n",
my_var3, my_var2)
}
Using a short variable declaration, we may specify several variables of
diverse types in a single declaration. The expression determines the
type of these variables.
Example:
// Program to show the
// concept of the variable
package main
import "fmt"
func main() {
// Using short-variable declaration
// Multiple variables of the different types
// are declared and initialized in the single line
my_var1, my_var2, my_var3 := 800, "Piiks", 58.26
// Display the value and type of variables
fmt.Printf("Value of my_var1 is : %d\n", my_var1)
fmt.Printf("Type of my_var1 is : %T\n", my_var1)
fmt.Printf("\nValue of my_var2 is : %s\n", my_var2)
fmt.Printf("Type of my_var2 is : %T\n", my_var2)
fmt.Printf("\nValue of my_var3 is : %f\n", my_var3)
fmt.Printf("Type of my_var3 is : %T\n", my_var3)
}
CONSTANTS
It is fixed, as the name CONSTANTS indicates; likewise, once the value of
a constant is specified in a programming language, it cannot change further.
Constants can be of any basic data type, including integer constants,
floating constants, character constants, and literal strings.
Example:
package main
import "fmt"
const Pie = 3.14
func main()
{
const POP = "PiiksofPiiks"
fmt.Println("Hello", everyone)
fmt.Println("Happy", Pie, "Day")
const Correct= true
fmt.Println("Go rules?", Correct)
}
Numeric Constant
Numeric constants are quantities that have a high degree of accuracy.
Operations that mix numeric types are not permitted in Go since it is a
statically typed language. A float64 or even an int32 cannot be added to an
int. It is, nevertheless, permissible to write 1e6*time. Second, consider
mathematics. Exp(1) or even 1(‘t’+2.0). Constants, unlike variables, behave
in Go like conventional numbers.
Numerical constants are classified into integer, floating-point, and
complex.
Integer Constant
45 : decimal
0312 : octal
0x3b : hexadecimal
20 : int
20u : unsigned int
20l : long
20ul : unsigned long
232 : Legal
225u : Legal
0xFeeL : Legal
068 : Illegal: 8 is not an octal digit
042UU : Illegal: cannot repeat a suffix
3.14149 : Legal
314149E-5L : Legal
String Literals
Go offers two types of string literals: “ ” (double-quote style) and ‘ ’(back-
quote).
Strings can be concatenated using the + and += operators.
They are plain characters, escape sequences, and universal characters,
characters in a string are similar to character literals.
This is an example of untyped.
String types with zero values are blank strings, which can be represented
in literal by “or”.
Strings of all kinds can be compared using operators like ==,!=, and (for
comparing of same types).
Syntax:
First example:
"hello, piiksofpiiks"
"hello, \
piiksofpiiks"
"hello " "piiks" "ofpiiks"
Second example:
package main
import "fmt"
func main()
{
const C = "POP"
var D = "PiiksofPiiks"
// Concat the strings.
var helloEveryone = C+ " " + D
helloEveryone += "!"
fmt.Println(helloEveryone)
// Compare the strings.
fmt.Println(C == "POP")
fmt.Println(D < C)
}
Boolean Constant
Constants of both types are string constants and Boolean constants. It
complies with the same rules as a string constant. The primary distinction is
that it includes two untyped constants: true and false.
Example:
package main
import "fmt"
const Pie = 3.14
func main()
{
const trueConst = true
// Type definition using type keyword
type my_Bool bool
var default_Bool = trueConst // allowed
var custom_Bool my_Bool = trueConst // allowed
// default_Bool = custom_Bool // not-allowed
fmt.Println(default_Bool)
fmt.Println(custom_Bool)
}
VARIABLE SCOPE IN Go
The scope of a variable may be defined as the area of the program where a
given variable is available. Declare a variable in a class, method, loop, or
other structure. All identifiers in GoLang, like those in C/C++, are lexically
(or statically) scoped, which implies that the variable’s scope is determined
at compilation time. On the other hand, a variable may only be called from
inside the code block in which it is defined.
Variable scope rules in GoLang are divided into two categories
depending on where the variables are declared:
Local Variables
Global Variables
Local Variables
Variables defined within a function or a block are local variables.
These are unreachable outside of the function or block.
These variables can also declare within for, while, and similar
expressions in a function.
On the other hand, retrieve these variables through nested code blocks
within a function.
These variables are also known as block variables.
A compile-time error will occur if these variables are declared twice
with the same name in the same scope.
These variables are no longer present when the function’s execution is
complete.
The variable defined outside the loop is accessible from within the
nested loops. It denotes that a global variable is accessible to all
functions and loops. The function’s loop and function will access the
local variable.
A variable declared within a loop body is invisible to the outside
world.
Example:
// Example of a program to demonstrate local
variables
package main
import "fmt"
// the main function
func main() { // the local level scope of the main
function begins here.
// inside the main function, local variables
var my_variable1, my_variable2 int = 90, 47
// Variable values are displayed
fmt.Printf("Value of my_variable1 is : %d\n",
my_variable1)
fmt.Printf("Value of my_variable2 is : %d\n",
my_variable2)
} // the main function's local level scope
terminates here.
Global Variables
Global variables are those defined outside of a function or a block.
These are only available for the length of the program.
These are declared at the program’s top, outside any functions or
blocks.
We can access these from any point in the program.
Example:
// To demonstrate the global variables, write a
program
package main
import "fmt"
// global-variable declaration
var my_variable1 int = 120
func main() { // from here the local level scope
begins
// the local variables inside main function
var my_variable2 int = 220
// Display the value of global variable
fmt.Printf("The Value of Global my_variable1 is :
%d\n",
my_variable1)
// Display value of the local variable
fmt.Printf("The Value of Local my_variable2 is :
%d\n",
my_variable2)
// calling the function
display()
} // local level scope ends
// taking function
func display() { // the local level begins
// Display the value of global variable
fmt.Printf("The Value of Global my_variable1 is :
%d\n",
my_variable1)
} // the local scope terminates here
Note: What happens if a function has a local variable with the same name
as a global variable?
The solution is simple: The compiler will prefer the local variable. The
compiler issues a compile-time error typically when two variables with
similar names are defined. The compiler will, however, accept variables
provided in different scopes. When a local variable with the same name as a
global variable is defined, the compiler will prioritize the local variable.
The output is shown in the example below. As my_variable1 in function,
main has a value of 220. As a result, a local variable has a high preference
over a global variable.
Example:
package main
import "fmt"
func main() {
var width, height int = 220, 60 //declaring
multiple-variables
fmt.Println("width :", width, "height :",
height)
}
Suppose the variables have an initial value, then remove the type. The int
type may delete because the variables in the previous program have starting
values.
Example:
package main
import "fmt"
func main() {
var width, height = 220, 60 //"int" is dropped
fmt.Println("width :", width, "height :",
height)
}
For example, the above program will produce a width of 220 and a height
of 60.
We should have figured that if no initial value for width and height is
specified, we will set them to 0.
Example:
package main
import "fmt"
func main() {
var width, height int
fmt.Println("width :", width, "height :",
height)
width = 220
height = 60
fmt.Println("new width :", width, "new height
:", height)
}
var (
nme1 = initialvalue1
nme2 = initialvalue2
)
Using the syntax explained above, the following program defines variables
of various types.
package main
import "fmt"
func main() {
var (
name = "harshita"
age = 23
height int
)
fmt.Println("my name :", name, ", age :", age, "and
height :", height)
}
SHORTHAND DECLARATION
Go also includes a more condensed method of defining variables. This is
known as a shorthand statement, and it uses the := operator.
Name := initialvalue is the shorthand for defining a variable.
Using the shorthand syntax, the following program declares a variable
count with a value of 12. Because count began with the integer value 12,
Go infers that it is of the type int.
package main
import "fmt"
func main() {
count := 14
fmt.Println("Count =",count)
}
The given code declares two variables of the types string and int.
If we run the software mentioned above, we will see my name, harshita,
and my age, 23, printed.
The assignment of initial values to all variables on the left side of the
assignment is required by shorthand declaration. The following program
will generate an assignment mismatch error since it has two variables but
only one value. This is since age has not been assigned a monetary value.
package main
import "fmt"
func main() {
name, age := "harshita" //error
fmt.Println("my name :", name, "age :", age)
}
Shorthand syntax may be used only when at least one of the variables on
the left side of := is newly declared. Consider the following program:
package main
import "fmt"
func main() {
c, d := 40, 20 // declare variables c and d
fmt.Println("c is", c, "d is", d)
c, d := 60, 30 // d is already declared but e is
new
fmt.Println("d is", d, "e is", e)
d, e = 20, 70 // assign new values to already
declared variables d and e
fmt.Println("changed d is", d, "e is", e)
}
OPERATORS IN Go
Operators are the fundamental building elements of all programming
languages. Consequently, the Go Language’s functionality is inadequate
without the use of operators. Operators enable us to perform a variety of
operations on operands. In the Go programming language, operators are
categorized depending on their capabilities:
Arithmetic Operators
Misc Operators
Relational Operators
Bitwise Operators
Logical Operators
Assignment Operators
Long Description
Operators in Go.
Arithmetic Operators
These are used in Go to perform arithmetic/mathematical operations on
operands:
Addition: The “+” operator connects two operands. For example, c+d.
Subtraction: The “−” operator adds two operands together. For
example, c−d.
Multiplication: The asterisk (*) is used to multiply the two operands.
For example, c*d.
Division: The “/” operator splits the first and second operands.
Consider the ratio c/d.
Modulus: The remainder of the “percent” operator is returned when
the first operand is divided by the second. For example, c percent d.
Example:
Relational Operators
Relational operators in Go are used to compare two values. Let’s take a
sneak peek at some of them:
The operator ‘==’ (Equal To) detects whether or not the two operands
are equal. If this is the case, the function then returns true. If it does
not, it returns false. For example, 9==9 will yield true.
The ‘!=’ operator determines if the two operands supplied are equal. It
returns true if it does not. If it does not, it returns false. It is the
boolean counterpart of the equals sign. For example, 9!=9 will yield
false.
If the first operand is greater than the second in the code, the
‘>’(Greater Than)operator is used. If this is the case, the function then
returns true. If it does not, it returns false. 9>6, for example, will return
true.
The (Less Than)operator detects if the first operand is less than the
second operand. If this is the case, the function then returns true. If it
does not, it returns false. 64, for example, will get a misleading result.
The ‘>=’ (Greater Than or Equal To)operator returns true if the first
operand is greater than or equal to the second operand. If it does not, it
returns false. For example, 9>=9 will yield true.
If the first operand is less than or equal to the second operand, the ‘<=’
(Least Than or Equal To)operator returns true. If it does not, it returns
false. For example, 9<=9 will likewise yield true.
Example:
// Program to show the use of the relational
operators
package main
import "fmt"
func main() {
c:= 38
d:= 25
// '=='(Equal To)
result1:= c == d
fmt.Println(result1)
// '!='(Not Equal To)
result2:= c != d
fmt.Println(result2)
// '<'(Less Than)
result3:= c < d
fmt.Println(result3)
// '>'(Greater Than)
result4:= c > d
fmt.Println(result4)
// '>='(Greater Than Equal To)
result5:= c >= d
fmt.Println(result5)
// '<='(Less Than Equal To)
result6:= c <= d
fmt.Println(result6)
}
Logical Operators
Logical operators in Go are used to combine two or more conditions or
enhance the original state’s evaluation.
Logical AND: When both requirements are met, the && operator
returns true. If it does not, it returns false. For example, c && d returns
true when both c and d are true (i.e., non-zero).
Logical OR: The ‘||’ operator returns true when one or both of the
prerequisites are satisfied. If it does not, it returns false. c || d, for
example, returns true if either c or d is true (i.e., non-zero). It returns
true if both c and d are true.
If the condition is not fulfilled, the ‘!’ operator returns true. If it does
not, it returns false. !c For example, returns true if it is false, i.e., when
c=0.
Example:
Bitwise Operators
Six bitwise operators function at the bit level or perform bit-by-bit
operations in the Go programming. The bitwise operators are as follows:
& (bitwise AND): Takes two operands and applies the AND operator
to each bit of the two integers. AND only returns 1 if both bits are 1.
| (bitwise OR): Takes two operands and applies the OR operator on
each bit of the two numbers. If either of the two bits is 1 OR produces
a result of 1.
^ (bitwise XOR): Takes two operands and applies the XOR function
on each bit of the two integers. If two bits are not the same, the
outcome of XOR is 1.
<< (left shift): The first operand’s bits are left-shifted, while the
second operand specifies the number of places to shift.
>> (right shift): Takes two integers, shifts the first operand’s bit to the
right, and the second operand determines the number of places to shift.
&^ (AND NOT): This is an easy-to-understand operator.
Example:
// A program that demonstrates the usage of bitwise
operators.
package main
import "fmt"
func main() {
c:= 46
d:= 85
// & (bitwise AND)
result1:= c & d
fmt.Printf("Result of c & d = %d", result1)
// | (bitwise OR)
result2:= c | d
fmt.Printf("\nResult of c | d = %d", result2)
// ^ (bitwise XOR)
result3:= c ^ d
fmt.Printf("\nResult of c ^ d = %d", result3)
// << (left shift)
result4:= c << 1
fmt.Printf("\nResult of c << 1 = %d", result4)
// >> (right shift)
result5:= c >> 1
fmt.Printf("\nResult of c >> 1 = %d", result5)
// &^ (AND NOT)
result6:= c &^ d
fmt.Printf("\nResult of c &^ d = %d", result6)
}
Assignment Operators
Assignment operators are used to assigning a value to a variable. The left
operand of the assignment operator is a variable, whereas the right operand
is a value. The right value must be of the same data type as the variable on
the left, or the compiler will raise an error. Assignment operators include
the following:
Example:
Misc Operators
&: This operator returns the address of the variable.
*: This operator returns the address of the variable.
<-: The name of this operator is received. Its purpose is to obtain a
value from the channel.
Example:
Sr.
No Statement & Description
if statement
1 In an, if statement, a boolean expression is followed by one or
more statements.
if...else statement
2 When the boolean expression is false, the if an optional else
statement follows statement.
nested if statements
3 If or else if statements can be used within other if or else if
statements.
switch statement
4 A switch statement compares a variable against a collection of
values for equality.
select statement
5 A select statement is similar to a switch statement, but case
statements interact with channel communications.
if Statement
This is the most direct decision-making statement. It is used to determine
whether or not a specific statement or block of statements will be executed;
for example, if a specified condition is true, then a block of statements is
run; otherwise, it is not.
Syntax:
if(condition)
{
// Statement to execute if condition is true
}
Long Description
Statement of if.
Example:
// A program to demonstrate the use of the if
statement
package main
import "fmt"
func main() {
// taking the local variable
var d int = 600
// using if statement for checking the condition
if(d < 1000) {
// print following if
// condition evaluates to true
fmt.Printf("d is less than 1000\n")
}
fmt.Printf("Value of d is : %d\n", d)
}
if…else Statement
If the condition is true, the if statement will execute a block of statements; if
the condition is false, the block of statements will not run. But what if the
condition is false and we want to do something different? This is when the
else statement comes into effect. When the condition is false, we may run a
code block using the else statement in conjunction with the if statement.
Syntax:
if (condition)
{
// executes this block if the
// condition true
} else {
// executes this block if the
// condition false
}
Long Description
Statement of if-else.
Example:
Nested if Statement
A nested if statement in Go is an if statement that is the target of another if
or else expression. A nested if statement is a statement nestled inside
another if statement. In GoLang, we may nest if statements within if
statements. In other words, we may nest one if the expression is inside
another.
Syntax:
if (condition1) {
// executes when the condition1 true
if (condition2) {
// executes when the condition2 true
}
}
Long Description
Statement of nested-if.
Example:
// Program to show the use of nested if statement
package main
import "fmt"
func main() {
// taking two local-variable
var d1 int = 700
var d2 int = 300
// using if statement
if( d1 == 700 ) {
// if condition is true then
// check the following
if( d2 == 800 ) {
// if the condition is true
// then display following
fmt.Printf("Value of d1 is 700 and d2 is
300\n" );
}
}
}
if..else..if Ladder
A user can choose from many options here. The if statements are executed
in the order specified. The statement linked with that if is performed when
one of the criteria is fulfilled, and the rest of the ladder is skipped. If none
of the conditions are satisfied, the final else statement is executed.
Important notes:
The if statement can be either zero or one, and it must come after any
other if statements.
The else if statement in an if statement can include zero to many other
if statements and must come before the otherwise clause.
If an else if statement succeeds, there is no need to test other if or else
statements.
Syntax:
if(condition_1) {
// this block will execute when condition_1
true
} else if(condition_2) {
// this block will execute when condition2 true
}
....
else {
// this block will execute when none
// of the condition is true
}
Long Description
Statement of if-else-if.
Example:
// Program to demonstrate
// the use of if..else..if ladder
package main
import "fmt"
func main() {
// taking local-variable
var d1 int = 700
// checking condition
if(d1 == 220) {
// if condition is true then
// display following */
fmt.Printf("Value of d1 is 220\n")
} else if(d1 == 350) {
fmt.Printf("Value of d1 is 350\n")
} else if(d1 == 410) {
fmt.Printf("Value of d1 is 410\n")
} else {
// if none of the conditions is true
fmt.Printf("The None of values is matching\n")
}
}
SWITCH STATEMENT
A switch statement illustrates a multiway branch statement. It provides an
efficient mechanism for moving execution to other code sections based on
the value of the expression. There are two kinds of switch statements in the
Go programming language:
Expression Switch
Type Switch
Expression Switch
The switch expression is similar to the switch statement in C, C++, and
Java. It enables us to conveniently redirect execution to different portions of
code dependent on the expression’s value.
Syntax:
Example:
// Program to demonstrate the notion of the
Expression switch statement
package main
import "fmt"
func main() {
// switch statement with the both
// optional statement, i.e, day:=3
// and the expression, i.e, day
switch day:=3; day{
case 1:
fmt.Println("Sunday")
case 2:
fmt.Println("Monday")
case 3:
fmt.Println("Tuesday")
case 4:
fmt.Println("Wednesday")
case 5:
fmt.Println("Thursday")
case 6:
fmt.Println("Friday")
case 7:
fmt.Println("Saturday")
default:
fmt.Println("Invalid")
}
}
Type Switch
A type switch is used when comparing types. The type that will compare to
the type in the switch expression is included in the case in this switch.
Syntax:
switch optstatement; typeswitchexpression{
case typelist 1: Statement
case typelist 2: Statement
…
default: Statement
}
Example:
ARRAYS
Arrays in the GoLang scripting language are comparable to those in other
languages. We may need to retain a collection of data of the same type in
the program, such as a list of students’ grades. In a program, this type of
collection is held by an Array. An array is a fixed-length sequence used in
memory to store similar elements. Because of their restricted length, an
array is not as popular in the Go Language as Slice.
An array can store zero or more than zero items. The array items are
indexed with the [] index operator and their zero-based positioning; thus,
the index of the first element is array[0], and the index of the last element is
array[len(array)−1].
Syntax:
or
Var array_name[length]Type
Example:
// Program to demonstrate how to
// build an array with the var keyword
// and access array elements with their index value.
package main
import "fmt"
func main() {
// Creation of array of string type
// Using var keyword
var myarr[3]string
// The elements are assigned using index
myarr[0] = "HEW"
myarr[1] = "Heyevryoneoworld"
myarr[2] = "Hey"
// accessing the elements of array
// Using index value
fmt.Println("The Elements of Array:")
fmt.Println("The Element 1 is: ", myarr[0])
fmt.Println("The Element 2 is: ", myarr[1])
fmt.Println("The Element 3 is: ", myarr[2])
}
Syntax:
array_name:= [length]Type{item1, item2,
item3,...itemN}
Example:
// A program to demonstrate how to build an array
using a shorthand declaration
// and how to retrieve the array's elements using a
for loop.
package main
import "fmt"
func main() {
// the shorthand declaration of array
arr:= [4]string{"hey", "hew", "Hey1431",
"Heyeveryoneworld"}
// Accessing the elements of
// array Using for loop
fmt.Println("Elements of the array:")
for d:= 0; d < 3; d++{
fmt.Println(arr[d])
}
}
Multidimensional Array
Although we are already aware that arrays are one-dimensional, we can
make a multidimensional array. Multidimensional arrays are arrays of the
same kind. In Go, we may create a multidimensional array by using the
following syntax:
array_name[Length1][Length2]..[LengthN]Type
As seen in the code below, we may use the Var keyword or a shorthand
declaration to create a multidimensional array.
Example:
Syntax:
Example:
// For sized-array
func function_name(variable_name [size]type){
// Code..
}
We may provide one or more dimensional arrays to the function using this
approach. Let us use an example here to demonstrate this point:
// A program that demonstrates how to pass an array as
an argument to a function.
package main
import "fmt"
// This function accept an
// array as argument
func myfun(c [5]int, size int) int {
var c, val, d int
for c = 0; c < size; c++ {
val += c[x]
}
d = val / size
return d
}
// main-function
func main() {
// Creating, initializing array
var arr = [5]int{37, 99, 29, 45, 24}
var rest int
// Passing array as an argument
rest = myfun(arr, 5)
fmt.Printf("The Final result is: %d ", rest)
}
SLICES
Slice is a more efficient, adaptable, and accessible Go data structure than an
array. Multiple components cannot be placed in the same slice because it is
a variable-length sequence of items of the same type. It is similar to an
array in that it has a length and an index value. Unlike an array, though, the
size of the slice is extended. A slice and an array are connected internally; a
slice refers to an underlying array. The slice may include duplicate
components. The first index point in a slice is always 0, and the last is
(length of slice − 1).
Slice Declaration
A slice is described in the same way as an array, except it does not specify
the size of the slice. As a result, it may grow and compress as needed.
Syntax:
[]D
or
[]D{}
or
[]D{value1, value2, value3, ...value n}
Slice Components
A slice is composed of three parts:
Example:
Example:
// program to demonstrate how to use a slice literal
to create a slice.
package main
import "fmt"
func main() {
// Creation of slice using var keyword
var myslice1 = []string{"Hey", "from",
"Everyone"}
fmt.Println("My Slice 1 is:", myslice1)
// Creation of a slice
//using shorthand-declaration
myslice2 := []int{34, 25, 67, 59, 31, 14, 75}
fmt.Println("My Slice 2 is:", myslice2)
}
Using an Array
We can construct a slice from the provided array since the slice is the
array’s reference. To make a slice from a given array, define the lower and
upper boundaries, implying that the slice can receive elements from the
array starting with the lower bound and ending with the upper bound. It
excludes the things in the upper bound mentioned above. As shown in the
following cases:
Syntax:
arrayname[low:high]
Example:
// program to demonstrate how to create array slices
package main
import "fmt"
func main() {
// Array-Creation
arry := [4]string{"Hey", "from", "Creator",
"HFC"}
// Creating slices from the given array
var myslice1 = aryr[1:2]
myslice2 := arry[0:]
myslice3 := arry[:2]
myslice4 := arry[:]
// Display-result
fmt.Println("My Array is: ", arry)
fmt.Println("My Slice 1 is : ", myslice1)
fmt.Println("My Slice 2 is: ", myslice2)
fmt.Println("My Slice 3 is: ", myslice3)
fmt.Println("My Slice 4 is: ", myslice4)
}
Syntax:
slicename[low:high]
Example:
// Program to show how to create slices from slice
package main
import "fmt"
func main() {
// Creating-slice
oRignAl_slice := []int{60, 10, 40, 80,
52, 39, 61}
// Creating slices from the given slice
var myslice1 = oRignAl_slice[1:5]
myslice2 := oRignAl_slice[0:]
myslice3 := oRignAl_slice[:6]
myslice4 := oRignAl_slice[:]
myslice5 := myslice3[2:4]
// Display-result
fmt.Println("Original Slice is:", oRignAl_slice)
fmt.Println("New Slice 1 is:", myslice1)
fmt.Println("New Slice 2 is:", myslice2)
fmt.Println("New Slice 3 is:", myslice3)
fmt.Println("New Slice 4 is:", myslice4)
fmt.Println("New Slice 5 is:", myslice5)
}
Syntax:
func make([]T, len, cap) []T
Example:
// Program to show how to create slices
// Using make-function
package main
import "fmt"
func main() {
// Creating array of the size 8 and slice this
array till 3
// and return the reference of slice
// Using make function
var myslice1 = make([]int, 3, 8)
fmt.Printf("Slice 1 = %v, \nlength = %d,
\ncapacity = %d\n",
myslice1, len(myslice1),
cap(myslice1))
// Creating another array of the size 8
// and return the reference of slice
// Using make-function
var myslice2 = make([]int, 9)
fmt.Printf("Slice 2 = %v, \nlength = %d,
\ncapacity = %d\n",
myslice2, len(myslice2),
cap(myslice2))
}
Example:
// program to show
// iterating over a slice using the for loop
package main
import "fmt"
func main() {
// Creating-slice
myslice := []string{"This", "is", "the",
"instance",
"of", "Go", "programing"}
// Iterate using for-loop
for d := 0; d < len(myslice); d++ {
fmt.Println(myslice[d])
}
}
Example:
// Program to demonstrate slice composite literal
package main
import "fmt"
func main() {
// Slice with composite literal
// Slice allows to group together
// values of the same type
// here the type of values is int
st1 := []int{33, 46, 29, 74}
// displaying-values
fmt.Println(st1)
}
Ints
This method only sorts an int slice, and the components in the slice are
sorted in ascending order.
Syntax:
func Ints(slc []int)
In this scenario, slc represents an int slice. Let us use an example here to
demonstrate this point:
//program that demonstrates how to sort a slice of
integers
package main
import (
"fmt"
"sort"
)
// main-function
func main() {
// Creating and initializing the slices
// Using shorthand-declaration
scl1 := []int{400, 200, 100, 700, 300, 600, 800}
scl2 := []int{-14, 167, -44, 79, 0, 32, -4}
// Displaying-slices
fmt.Println("Slices(Before):")
fmt.Println("Slice 1 is: ", scl1)
fmt.Println("Slice 2 is: ", scl2)
// Sortingslice of ints
// Using the Ints function
sort.Ints (scl1)
sort.Ints (scl2)
// Displaying-result
fmt.Println("\nSlices(After):")
fmt.Println("Slice 1 is: ", scl1)
fmt.Println("Slice 2 is: ",scl2)
}
IntsAreSorted
This function determines if the provided slice of ints is sorted or not (in
increasing order). This method then returns true if the slice is sorted; else, it
returns false.
Syntax:
In this scenario, scl represents an int slice. Let us use an example here to
demonstrate this point:
// program that demonstrates
// how to determine whether a given
// slice of ints is in sorted form or not.
package main
import (
"fmt"
"sort"
)
// main-function
func main() {
// Creating and initializing the slices
// Using shorthand-declaration
scl1 := []int{400, 200, 100, 700, 300, 600, 800}
scl2 := []int{-14, 167, -44, 79, 0, 32, -4}
// Displaying-slices
fmt.Println("Slices:")
fmt.Println("Slice 1 is: ", scl1)
fmt.Println("Slice 2 is: ", scl2)
// Checking the slice is in the sorted form or not
// Using the IntsAreSorted function
rest1 := sort.IntsAreSorted(scl1)
rest2 := sort.IntsAreSorted(scl2)
// Displaying-result
fmt.Println("\nResult:")
fmt.Println("Is Slice1 is sorted?: ", rest1)
fmt.Println("Is Slice2 is sorted?: ", rest2)
}
Syntax:
func Trim(ori_slice[]byte, cut_string string) []byte
ori_slice is the original slice of bytes, and cut_string indicates a string that
we want to trim in the provided slice. Let’s dig deep using the example
given:
// program to demonstrate the concept of trim in a byte
slice
package main
import (
"bytes"
"fmt"
)
func main() {
// Creating and initializing
// the slice of bytes
// Using shorthand-declaration
slice_1 := []byte{'!', '!', 'H', 'e', 'y', 'y',
'f', 'r',
'o', 'm', 'W', 'o', 'r', 'l', 'd',
'#', '#'}
slice_2 := []byte{'*', '*', 'L', 'i', 'c', 'h',
'i', '^', '^'}
slice_3 := []byte{'%', 'h', 'e', 'l', 'l', 'o',
'%'}
// Displaying slices
fmt.Println("Original-Slice:")
fmt.Printf("Slice 1 is: %s", slice_1)
fmt.Printf("\nSlice 2 is: %s", slice_2)
fmt.Printf("\nSlice 3 is: %s", slice_3)
// Trimming specified leading
// and trailing the Unicodes points
// from the given slice of bytes
// Using the Trim function
rest1 := bytes.Trim(slice_1, "!#")
rest2 := bytes.Trim(slice_2, "*^")
rest3 := bytes.Trim(slice_3, "@")
// Display-results
fmt.Printf("New Slice:\n")
fmt.Printf("\nSlice 1 is: %s", rest1)
fmt.Printf("\nSlice 2 is: %s", rest2)
fmt.Printf("\nSlice 3 is: %s", rest3)
}
Syntax:
func Split(o_slice, sep []byte) [][]byte
Example:
// Program to demonstrate the concept of splitting a
slice of bytes
package main
import (
"bytes"
"fmt"
)
func main() {
// Creating and initializing slice of bytes
// Using shorthand-declaration
slice_1 := []byte{'!', '!', 'H', 'e', 'y', 'y',
'f',
'r', 'o', 'm', 'W', 'o', 'r', 'l', 'd', '#',
'#'}
slice_2 := []byte{'L', 'i', 'c', 'h', 'i'}
slice_3 := []byte{'%', 'h', '%', 'e', '%', 'l',
'%', 'l', '%', 'o', '%'}
// Displaying-slices
fmt.Println("Original-Slice:")
fmt.Printf("Slice 1 is: %s", slice_1)
fmt.Printf("\nSlice 2 is: %s", slice_2)
fmt.Printf("\nSlice 3 is: %s", slice_3)
// Splitting the slice of bytes
// Using Split function
rest1 := bytes.Split(slice_1, []byte("iik"))
rest2 := bytes.Split(slice_2, []byte(""))
rest3 := bytes.Split(slice_3, []byte("%"))
// Display results
fmt.Printf("\n\nAfter-splitting:")
fmt.Printf("\nSlice 1 is: %s", rest1)
fmt.Printf("\nSlice 2 is: %s", rest2)
fmt.Printf("\nSlice 3 is: %s", rest3)
}
STRING
Strings in Go are distinct from those in other languages, such as Java, C++,
Python, etc. It’s a string of variable-width characters, each represented by
one or more UTF-8-encoded bytes. Strings are either an immutable chain of
arbitrary bytes or a read-only slice of bytes whose bytes may be represented
in Unicode text using UTF-8 encoding.
Because of UTF-8 encoding, a GoLang string may include material mash-
up of every language globally without generating confusion or restricting
the page. Strings are typically enclosed in double-quotes “”, as shown in the
given an example:
String Literals
In the Go programming language, string literals are created in two ways:
Escape
Character Description
\\ Backslash
Unicode character with the given 3-digit 8-bit octal
\000
code point
Single quote (‘). It is allowed inside the character
\’
literal
Double quote (“). It is allowed inside the interpreted
\”
string literal
\a ASCII bell
\b ASCII backspace
\f ASCII formfeed
\n ASCII linefeed
\r ASCII carriage return
\t ASCII tab
Unicode character with the given 4-digit 16-bit hex
\uhhhh
code point
Unicode character with the given 8-digit 32-bit hex
code point
\v ASCII vertical tab
Escape
Character Description
Unicode character with the given 2-digit 8-bit hex
\xhh
code point
Using Backticks(")
Backticks(") are used to form string literals, also known as raw literals. Raw
literals do not support escape characters, span several lines, or include
characters other than the backtick. It is frequently used to generate multiline
messages, regular expressions, and HTML.
Example:
// Program to show string literals
package main
import "fmt"
func main() {
// Creating, and initializing a
// variable with the string literal
// Using double-quote
My_value_1 := "Welcome to Home"
// Adding escape character
My_value_2 := "Welcome!\nHome "
// Using backticks
My_value_3 := 'Hello!Everybody'
// Adding escape-character
// in the raw literals
My_value_4 := 'Hello!\nPiiksforPiiks'
// Displaying-strings
fmt.Println("The String 1 is: ", My_value_1)
fmt.Println("The String 2 is: ", My_value_2)
fmt.Println("The String 3 is: ", My_value_3)
fmt.Println("The String 4 is: ", My_value_4)
}
Trim
This method trims the text by deleting all of the stored procedure leading
and trailing Unicode code points.
Syntax:
Example:
// program that demonstrates how to trim string
package main
import (
"fmt"
"strings"
)
// main-method
func main() {
// Creating and initializing the string
// Using shorthand-declaration
stry1 := "!!Welcome to Home!!"
stry2 := "@@This is a example of GoLang$$"
// Displaying -trings
fmt.Println("Strings-before-trimming:")
fmt.Println("String 1 is: ", stry1)
fmt.Println("String 2 is:", stry2)
// Trimming-given-strings
// Using the Trim() function
rest1 := strings.Trim(stry1, "!")
rest2 := strings.Trim(stry2, "@$")
// Displaying-results
fmt.Println("\nStrings-after-trimming:")
fmt.Println("Result 1 is: ", rest1)
fmt.Println("Result 2 is:", rest2)
}
TrimLeft
TrimLeft is a function that trims the string’s Unicode code points on the left
side.
Syntax:
Example:
// program to demonstrate how to trim the left-hand
side components of a string
package main
import (
"fmt"
"strings"
)
// main-method
func main() {
// Creating, and initializing the string
// Using shorthand-declaration
stry1 := "!!Welcome to home **"
stry2 := "@@This is a example of GoLang$$"
// Displaying-strings
fmt.Println("Strings-before-trimming:")
fmt.Println("String 1 is: ", stry1)
fmt.Println("String 2 is:", stry2)
// Trimming-given-strings
// Using-TrimLeft()-function
rest1 := strings.TrimLeft(str1, "!*")
rest2 := strings.TrimLeft(str2, "@")
// Displaying-results
fmt.Println("\nStrings after the trimming:")
fmt.Println("Result 1 is: ", rest1)
fmt.Println("Result 2 is:", rest2)
}
TrimRight
This method cuts the string’s right-hand side Unicode code points (specified
in the function).
Syntax:
Example:
// Program to show how to
// trim the right-hand side elements from the string
package main
import (
"fmt"
"strings"
)
// main-method
func main() {
// Creating, and initializing the string
// using shorthand declaration
stry1 := "!!Welcome to Home **"
stry2 := "@@This is an example of GoLang$$"
// Displaying-strings
fmt.Println("Strings-before-trimming:")
fmt.Println("String 1 is: ", stry1)
fmt.Println("String 2 is:", stry2)
// Trimming given strings
// Using TrimRight() function
rest1 := strings.TrimRight(stry1, "!*")
rest2 := strings.TrimRight(stry2, "$")
// Displaying-results
fmt.Println("\nStrings after trimming:")
fmt.Println("Result 1 is: ", rest1)
fmt.Println("Result 2 is:", rest2)
}
TrimSpace
This method eliminates all white space from the provided string, including
the leading and following white space.
Syntax:
func TrimSpace(str string) string
Example:
TrimSuffix
The trailing suffix of the string is removed using this procedure. If the given
string does not include the requested suffix string, this function returns the
original text without modification.
Syntax:
func TrimSuffix(str, suffstr string) string
Example:
TrimPrefix
The leading prefix of the string is removed using this procedure. If the input
string does not include the desired prefix string, this function returns the
original string without modification.
Syntax:
func TrimPrefix(str, suffstr string) string
Example:
// program to demonstrate how to trim a prefix
string from a specified string.
package main
import (
"fmt"
"strings"
)
// Main-method
func main() {
// Creating, and initializing the string
// Using-shorthand-declaration
stry1 := "Welcome, Home"
stry2 := "This is an example of GoLang"
// Displaying the strings
fmt.Println("Strings-before-trimming:")
fmt.Println("String 1 is: ", stry1)
fmt.Println("String 2 is: ", stry2)
// Trimming prefix string from the given strings
// Using-TrimPrefix()-function
rest1 := strings.TrimPrefix(str1, "Hello")
rest2 := strings.TrimPrefix(str2, "Everyone")
// Displaying-results
fmt.Println("\nStrings-after-trimming:")
fmt.Println("Result 1 is: ", rest1)
fmt.Println("Result 2 is: ", rest2)
}
Split
This function divides the text into all substrings separated by the provided
separator and returns a slice holding these substrings.
Syntax:
func Split(str, sep string) []string
The string str is utilized here, as is the separator sep. If str does not
include the specified sep and sep is not empty, it will produce a slice of
length 1 containing only str. The code will divide after each UTF-8
sequence if the sep option is not specified. If both str and sep are empty, it
will generate an empty slice.
Example:
// Program to show how to split a string
package main
import (
"fmt"
"strings"
)
// main-function
func main() {
// Creating, and initializing string
stry1 := "Welcome, to, our channel,
Helloeverybody"
stry2 := "My pet name is Bruno"
stry3 := "I love to play cricket"
// Displaying-strings
fmt.Println("String 1 is: ", stry1)
fmt.Println("String 2 is: ", stry2)
fmt.Println("String 3 is: ", stry3)
// Splitting the given strings
// Using-Split()-function
rest1 := strings.Split(stry1, ",")
rest2 := strings.Split(stry2, "")
rest3 := strings.Split(stry3, "!")
rest4 := strings.Split("", "Helloeverybody,
hello")
// Displaying result
fmt.Println("\nResult 1 is: ", rest1)
fmt.Println("Result 2 is: ", rest2)
fmt.Println("Result 3 is: ", rest3)
fmt.Println("Result 4 is: ", rest4)
}
SplitAfter
Splits a string into all substrings following each iteration of the given
separator and returns a slice with these substrings.
Syntax:
func SplitAfter(str, sep string) []string
Example:
SplitAfterN
After each usage of the given separator, split a string into all substrings and
provide a slice comprising these substrings.
Syntax:
func SplitAfterN(str, sep string, m int) []string
Example:
// Program that shows how to split a string
package main
import (
"fmt"
"strings"
)
// main-function
func main() {
// Creating, and initializing string
stry1 := "Welcome, to online session,
Heyeveryone"
stry2 := "My pet name is fluffy"
stry3 := "I love to bake cake"
// Displaying-strings
fmt.Println("String 1 is: ", stry1)
fmt.Println("String 2 is: ", stry2)
fmt.Println("String 3 is: ", stry3)
// Splitting-given-strings
// Using the SplitAfterN() function
rest1 := strings.SplitAfterN(stry1, ",", 2)
rest2 := strings.SplitAfterN(stry2, "", 4)
rest3 := strings.SplitAfterN(stry3, "!", 1)
rest4 := strings.SplitAfterN("", "Heyeveryone,
hello", 3)
// Displaying-result
fmt.Println("\nResult 1 is: ", rest1)
fmt.Println("Result 2 is: ", rest2)
fmt.Println("Result 3 is: ", rest3)
fmt.Println("Result 4 is: ", rest4)
}
MAPS
A map is a powerful, innovative, and flexible data structure in the Go
computer language. Maps in the GoLang programming language are a
collection of unordered key-value pairs. It’s popular because it provides
speedy lookups and values that can be obtained, modified, or deleted using
keys.
Simple
We may create and initialize a map in this manner without using the make()
function:
// Empty-map
map[KeyType]ValueType{}
// Map with keyvalue pair
map[KeyType]ValueType{key1: value1, ..., keyN:
valueN}
Example:
var mymap map[int]string
A map’s zero value in maps is nil. Therefore, a nil map has no keys.
The compiler will detect a runtime error by adding a key-value pair
into the nil map.
Example:
Syntax:
make(map[KeyType]ValueType, initial_Capacity)
make(map[KeyType]ValueType)
Example:
// Program to show how to
// create, and initialize map
// and using make() function
package main
import "fmt"
func main() {
// Creation of map
// using make() function
var Mymap = make(map[float64]string)
fmt.Println(Mymap)
// As we all know, the make() method always
returns a map that has been initialized
// We can include values in it
Mymap[1.3] = "Nidhi"
Mymap[1.5] = "Sunidhi"
fmt.Println(Mymap)
}
DEFER KEYWORD
In the Go programming language, defer instructions postpone the execution
of a function, method, or anonymous method until the neighboring
functions return. In other words, deferred function or method call arguments
evaluate instantly but do not run until the nearby function returns. We may
create a delayed method, function, or anonymous function using the defer
keyword.
Syntax:
// Function
defer func func-name(parameter_list
Type)return_type{
// Code
}
// Method
defer func (receiver Type)
method_name(parameter_list){
// Code
}
defer func (parameter_list)(return_type){
// code
}()
Example:
// Program to show
// concept of defer statement
package main
import "fmt"
// Function
func mul(d1, d2 int) int {
rest := d1 * d2
fmt.Println("Result: ", rest)
return 0
}
func show() {
fmt.Println("Hello, Everybody")
}
// main-function
func main() {
// Calling mul() function
// Here mul function behaves
// like normal-function
mul(43, 25)
// Calling mul()function
// Using the defer keyword
// Here mul() function is defer-function
defer mul(27, 46)
// Calling the show() function
show()
}
PANIC IN GoLang
In the Go scripting language, panic, like an exception, happens during
runtime. In other words, panic happens when an unforeseen condition arises
in our Go program, resulting in the program’s execution being terminated.
When a specific circumstance exists, such as out-of-bounds array accesses,
panic can occur during runtime, as shown in example.
Syntax:
Example:
RECOVER
The recover function in Go is used to manage panic in the same way that
try/catch blocks capture exceptions in languages, such as Java, C#, and
others. It is a built-in function defined in the built-in package of the Go
programming language. This approach is mainly used to re-establish control
of a panicked goroutine. In other words, it addresses the goroutine’s frantic
behavior.
Syntax:
Example:
CLOSURES
The Go scripting language has an anonymous function. An anonymous
function can form a closure. A closure is anonymous function that refers to
variables defined outside the function. It is similar to accessing global
variables available before the function’s declaration.
Example:
Syntax:
func(parameter-list)(return-type){
// code call same function
// within function for recursion
// Use return statement only
// if return-type are given.
return
}()
Example:
// Program to show
// how to create recursive Anonymous-function
package main
import "fmt"
func main() {
// Anonymous-function
var recursiveAnonymous func()
recursiveAnonymous = func() {
// Printing message to show the
// function call and iteration
fmt.Println("Anonymous functions could be
recursive.")
// Calling same function
recursively
recursiveAnonymous()
}
// main calling of function
recursiveAnonymous()
}
POINTERS
In the Go computer language or GoLang, pointers are variables that retain
the memory address of another variable. In GoLang, pointers are known as
special variables. Variables are used in the system to keep data at a
specified memory address. Memory addresses are always written in
hexadecimal (starting with 0x like 0xFFAAF, etc.).
Example:
Declaring a Pointer
Example: Consider the string pointer as shown below, which can only
hold the memory locations of string variables.
var st *string
Pointer Initialization
To do this, use the address operator to establish a pointer with the memory
address of another variable, as seen in the following example:
// normal variable-declaration
var c = 95
// Initialization of the pointer st with
// memory address of the variable c
var st *int = &c
Example:
// program to show declaration and initialization
package main
import "fmt"
func main() {
// taking normal-variable
var c int = 3998
// declaration of the pointer
var d *int
// initialization of the pointer
d = &c
// displaying-result
fmt.Println("Value stored in c = ", c)
fmt.Println("Address of c = ", &c)
fmt.Println("Value stored in variable d = ", d)
}
Dereferencing a Pointer
The * operator is also known as the dereferencing operator. It is used to
specify the pointer variable and to access the value of a variable to which
the pointer points, a process known as indirecting or dereferencing. The
value at the location is sometimes referred to as the * operator. Let’s look at
an example to help us comprehend this concept:
// Program to illustrate
// the concept of dereferencing a pointer
package main
import "fmt"
func main() {
// using the var keyword
// we are not defining any type with the variable
var x = 328
// taking pointer variable using
// the var keyword without specifying the type
var a = &x
fmt.Println("The Value stored in x = ", x)
fmt.Println("The Address of x = ", &x)
fmt.Println("The Value stored in pointer variable a
= ", a)
// this is dereferencing a pointer
// using * operator before the pointer
// variable to access value stored at the variable
at which it is pointing
fmt.Println("The Value stored in y(*a) = ", *a)
}
Instead of assigning a new value to the variable, we can alter the value of
the pointer or memory location.
Example:
Example: In this case, we can see that we are instantiating a Struct with
the new keyword.
// Program to show how to instantiate Struct
// using a new keyword
package main
import "fmt"
type emp struct {
name string
empid int
salary int
}
func main() {
// emp1 is a pointer to an instance of emp
// using the new keyword
emp1 := new(emp)
emp1.name = "ABC"
emp1.empid = 2325
emp1.salary = 37000
fmt.Println(emp1)
// emp2 is an instance of emp
var emp2 = new(emp)
emp2.name = "XYZ"
emp2.salary = 40000
fmt.Println(emp2)
}
Pointers to a Function
Pointers are variables in the Go programming language, or GoLang used to
hold the memory address of another variable. We may also pass pointers to
the function in the same way variables are. There are two ways to
accomplish this.
Note: The variables and pointers in the preceding programs can also be
declared using the short declaration operator(:=).
Pointer to a Struct
A pointer is a variable that stores the memory address of another variable.
Pointers are also known as special variables in GoLang. The variables are
used to store data in the system at a specific memory address.
A pointer to a struct can also use. In GoLang, a struct is a user-defined
type that allows us to group/combine elements of possibly diverse kinds
into a single type. To utilize a pointer to a struct, use the & operator, also
known as the address operator. GoLang allows programmers to use pointers
to access the fields of a structure without explicitly dereferencing it.
Pointer to Pointer in Go
In the Go programming language or GoLang, pointers are variables that
retain the memory address of another variable. A pointer is a particular
variable that may point to any variable, even another pointer. Essentially,
this looks to be a pointer chain. When we define a pointer to a pointer, the
address of the second pointer is stored in the first pointer. This notion is also
known as double pointers.
Example: In the program below, the pointer pt2 saves the location of
the pointer pt1. Dereferencing pt2, i.e., *pt2 returns the address of
variable V or the value of pointer pt1. If we attempt **pt2, you will get
the value of the variable V, which is 200.
// Program to illustrate
// the concept of the Pointer to Pointer
package main
import "fmt"
// the main Function
func main() {
// taking variable
// of the integer type
var V int = 200
// taking a pointer
// of integer type
var pt1 *int = &V
// taking pointer to
// pointer to pt1
// storing the address
// of pt1 into pt2
var pt2 **int = &pt1
fmt.Println("Value of Variable V is = ", V)
fmt.Println("The Address of variable V is = ",
&V)
fmt.Println("Value of pt1 is = ", pt1)
fmt.Println("The Address of pt1 is = ", &pt1)
fmt.Println("Value of pt2 is = ", pt2)
// Dereferencing pointer to pointer
fmt.Println("The Value at the address of pt2 is
or *pt2 = ", *pt2)
// double pointer will give the value of
variable V
fmt.Println("*(The Value at the address of pt2
is) or **pt2 = ", **pt2)
}
Comparing Pointers
Pointers are variables in the Go scripting language, or GoLang, holding
another variable’s memory address. In GoLang, pointers are sometimes
referred to as special variables. The variables are used to keep data in the
system at a specified memory address. Memory addresses are always in
hexadecimal format (starting with 0x like 0xFFAAF, etc.).
In the Go scripting language, we may compare two pointers. Two pointer
values are only identical if they point to the same memory address or are
nil. We may compare pointers using the == and != operators provided by
the Go programming language.
== operator
If both pointers refer to the same variable, this function returns true. Return
false if both pointers are pointing to separate variables.
Syntax:
pointer1 == pointer2
Example:
!= operator
This operator returns false if both pointers lead to the same variable. Return
true instead if both pointers refer to different variables.
Syntax:
pointer_1 != pointer_2
Example:
GoLang STRUCTURES
In GoLang, a structure, also known as a struct, is a user-defined type that
allows us to group/combine parts of possibly divergent kinds into a single
type. A struct is a set of properties/fields that may represent any real-world
entity. This idea is related to classes in general in object-oriented
programming. It’s a straightforward class that doesn’t enable inheritance but
does allow for composition.
Declaring a structure:
type Address struct {
name string
streetno string
city string
state string
Pin-code int
}
Example:
Pointers to a Struct
Pointers are variables in the Go programming language, or GoLang, that
store the memory address of another variable. We may also refer to a struct,
as seen in the following example:
Syntax:
Example:
GoLang METHODS
With one exception, Go methods are equivalent to Go functions in
implementation. A receiver parameter is included in the procedure. The
receiver parameter allows the method to access the attributes of the
receiver. In this case, the recipient might be either struct or non-struct. We
must maintain the receiver and receiver type in the same package while
writing code. Furthermore, we are not permitted to write a method whose
receiver type has already been specified in another package, including built-
in kinds, such as int, string, etc. If we attempt, the compiler will generate an
error.
Syntax:
func(reciver-name Type) method-name(parameter-list)
(return-type){
// Code
}
Example:
Syntax:
func (p *Type) method-name(...Type) Type {
// Code
}
Example:
// Program to illustrate pointer receiver
package main
import "fmt"
// authorstructure
type author struct {
name string
branch string
particles int
}
// Method with the receiver of author type
func (d *author) show(abranch string) {
(*d).branch = abranch
}
// main-function
func main() {
// Initializing-values of author structure
rest := author{
name: "Harsh",
branch: "HRF",
}
fmt.Println("The Author's name is: ", rest.name)
fmt.Println("The Branch Name(Before) is: ",
rest.branch)
// Creating-pointer
p := &rest
// Calling show-method
p.show("KIH")
fmt.Println("The Author's name is: ", rest.name)
fmt.Println("The Branch Name(After) is: ",
rest.branch)
}
INTERFACES
The Go Language’s interfaces are distinct from those of other languages. In
Go, an interface is a specific type that expresses a collection of one or more
method signatures. Because the interface is abstract, we cannot create an
instance of it. However, we are authorized to create an interface type
variable that may be assigned with a concrete type value with the interface’s
needed methods. In other words, the interface is a collection of methods and
a custom type.
Example:
// Creating-interface
type myinterface interface{
// Methods
func1() int
func2() float64
}
The type and interface keywords enclose the interface name, whereas curly
brackets enclose the method signatures.
Example:
Redundant Functions
Assume we have a pet package (in other languages, a “package” is
analogous to a “library”) that contains Dogs and Cats types. Dogs use the
Fetch method, cats use the Purr method, and the Walk and Sit both dogs and
cats use techniques.
package pets
import "fmt"
type Dogs struct {
Name string
Breed string
}
func (d Dogs) Walk() {
fmt.Println(d.Name, "walks across the hall")
}
func (d Dogs) Sit() {
fmt.Println(d.Name, "sits-down")
}
func (d Dogs) Fetch() {
fmt.Println(d.Name, "fetches-toy")
}
type Cats struct {
Name string
Breed string
}
func (c Cats) Walk() {
fmt.Println(c.Name, "walks across the hall")
}
func (c Cats) Sit() {
fmt.Println(c.Name, "sits-down")
}
func (c Cats) Purr() {
fmt.Println(c.Name, "purrss")
}
INHERITANCE
Inheritance, which entails inheriting the superclass’s attributes into the base
class, is one of the essential concepts in object-oriented programming.
Because GoLang lacks classes, inheritance is achieved using struct
embedding. We cannot extend structs directly but must instead utilize a
concept known as composition, in which the struct is used to construct more
objects. As a result, there is no idea of inheritance in GoLang.
Base structs can be embedded in a child struct in composition, and the
base struct’s methods can be called directly on the child struct, as seen in
the following example:
// Program to demonstrate the concept of inheritance
package main
import (
"fmt"
)
// declaring-struct
type Comic struct{
// declaring struct-variable
Universe string
}
// function to return the universe of comic
func (comic Comic) ComicUniverse() string {
// returns comic-universe
return comic.Universe
}
// declaring struct
type Marvel struct{
// anonymous field,
// this is composition where the struct is embedded
Comic
}
// declaring-struct
type DC struct{
// anonymous-field
Comic
}
// main-function
func main() {
// creating-instance
cs1 := Marvel{
// child struct can directly access base struct
variables
Comic{
Universe: "MCU",
},
}
// child struct can directly access the base struct
methods printing base method using the child
fmt.Println("Universe is:",
cs1.ComicUniverse())
cs2 := DC{
Comic{
Universe : "DC",
},
}
// printing the base method using child
fmt.Println("The Universe is:",
cs2.ComicUniverse())
}
DOI: 10.1201/9781003309055-2
IN THIS CHAPTER
➢ Refactoring
➢ Error handling
➢ Writing tests
In the previous chapter, we covered all the basic concepts of GoLang. This
chapter will discuss Test-Driven Development (TDD) with Go, where we
covered error handling, refactoring, and writing tests.
First, let’s talk about the procedure. TDD is a programming paradigm
that isn’t specific to Go but is quite popular among Go developers.
When developing a program to perform anything (say, multiply two
integers), one must first write a test.
A test is also a program, but it is designed to run another program with
various inputs and examine the outcome. The test ensures that the software
behaves as expected.
What’s remarkable about the TDD methodology is that we create the test
first before the code is tested (this is frequently referred to as test-first
development). So, why are we doing this?
By developing the test first, we are forced to think explicitly about how
the program should behave and describe those requirements as executable
code with great precision.
Because we’ll be calling our program from the test, we’ll also need to
create its API. So, contrary to its name, TDD isn’t actually about testing.
Instead, it’s a tool for thinking, a method for creating well-structured
systems with decent APIs. Because we are our own first users, we can
quickly identify when the code isn’t convenient or comfortable to use, and
we can improve it.
Let’s examine how we may use that method now that we’ve begun
working on our calculator app.
MAKING Go FILES
A friendly colleague from Texio Instronics has provided us with some Go
code that implements some of the calculator’s functionality and a test for it,
so we don’t have to start from blank. We’ll add that code to our project
folder in this section.
First, create a calculator file with our code editor in the calculator folder.
Then copy and paste the code in the below part:
Don’t worry about knowing all of the code at this point; we’ll go over it in
depth later. For the time being, simply enter this code into the calculator. Go
ahead and save it.
Create a new file called calculator_test.go together with the following:
package calculator_test
import (
"calculator"
"testing"
)
RUN TESTS
Run the following command while still in the calculator folder:
go test
PASS
ok calculator 0.234s
go test
Then it invokes the Subtract function with the numbers 6, 3 and saves the
result in another variable obtained:
got := calculator.Subtract(6, 3)
After obtaining this pair of variables, the goal is to compare wish to get and
discover if they vary. If they are, the Subtract function is not functioning
properly, and the test fails:
if want != got {
t.Errorf("want %f, got %f", want, got)
}
If we find an error in the Subtract function, try modifying the code to solve
it. Rerun the go test to ensure we got it properly. If we’re experiencing
problems, try modifying the numbers that Subtract is called with to see if
we can figure out what’s going wrong.
We can proceed once the exam has been passed.
When we’re finished, running the tests should result in the following
compilation error:
undefined: calculator.Multiply
Perfect! Now we’re ready to put Multiply into action for real. We’ll know
when we’ve nailed it when our failed test begins to pass. And then we may
come to a halt!
Conflicts Resolve
When GoLand detects issues with our refactoring, it displays a dialog with
a list of conflicts and brief explanations.
Click Continue to dismiss the error and open the preview in the Find tool
window.
To open the conflict items in the Find tool window and deal with them
further, click Show Conflicts in View.
For example, we may try to exclude an entry from refactoring by
pressing Delete, or we can cancel and return to the editor by clicking
Cancel.
Adjust the refactoring choices and click OK on the Code Editing page’s
Refactoring section.
Method of Extraction
This is an approach we commonly use in my programming. It entails
removing a chunk of code that has been grouped by intention and moving it
into a new method. We’ve discovered one compelling reason to use it: it
allows us to divide a big method or function into shorter methods that group
a piece of logic. In most cases, the name of the little method or function
offers a clearer understanding of what that bit of logic is.
The following example displays the before and after effects of this
refactoring method. My primary objective was to abstract complexity by
breaking it down into distinct functions.
2 https://wawand.co/blog/posts/four-most-refactoring-techniques-i-use/
Method of Move
After using the extract method refactoring, I sometimes wonder if this
function should belong to this struct or package. Move Method is a
straightforward approach that involves (as the name says) transferring a
method from one struct to another. One way I’ve discovered for
determining if a method should belong to that struct is to see if it accesses
the internals of another struct dependence more than its own. Consider the
following example:
The Books type obtains cohesiveness after using this technique since it is
the only one that has control and access to its fields and internal attributes.
Again, this mental process precedes a conscious action, understanding the
repercussions of refactoring.
Create a Parameter Object
How often have we encountered a method like this with a long list of
parameters?
Even though we don’t see the code within the function, we may imagine
how many operations it does when we see many parameters like this.
Sometimes we notice that such parameters are significantly connected to
one another and are utilized together afterward within the method where
they were declared. This refactoring involves grouping those arguments into
a struct, changing the method signature to use that object as a parameter,
and using that object inside the function to handle that circumstance more
object-oriented.
if strings.Contains(input, ";") {
nm1 := toNumber(input[:strings.Index(input, ";")])
nm2 := toNumber(input[strings.Index(input, ";")+1:])
return toNumber(input)
}
What exactly does that symbol imply? If the response is too vague for us,
we may create a temporary variable and set the value with the hard-coded
character to give it identity and intent.
return toNumber(input)
}
func main() {
args := os.Args[1:]
inFile := args[0]
inHash := args[1]
As a result, we’ve already discovered some issues. We’d also like to isolate
the checking logic and develop tests for it.
func main() {
// check for the missing parameters
if len(os.Args) != 3 {
fmt.Println("Error occured: missing-parameters")
// emit non-zero exit code
os.Exit(1)
}
// continue..
}
to that:
color.Green("%s has SHA256 %s", inFile, inHash)
hasher := sha256.New()
if _, err := io.Copy(hasher, reader); err != nil {
return false, "", err
}
calculatedHash = hex.EncodeToString(hasher.Sum(nil))
return hash == calculatedHash, calculatedHash, nil
}
Write a Test
We must now develop tests for the code because it is testable.
if err != nil {
t.Error(err)
} else if !isValid {
t.Error("hashes don't match", calculatedHash)
}
}
So, in the above code, we generate a byte slice containing the hex codes
0xbeef1010cafe, an io.Reader from it and compute its hash.
Error handling.
Compared to other major languages, such as JavaScript, Java, and Python,
error handling in GoLang is unusual. This can make it difficult for
inexperienced programmers to understand GoLang’s approach to error
handling.
In this session, we’ll look at how to handle failures using built-in GoLang
capabilities, how to extract information from errors, and the best practices
for doing so.
Errors in GoLang
Errors indicate that an undesirable circumstance is occurring in our
application. Assume we wish to establish a temporary directory to store
some files for our program.3 However, the directory creation fails. Because
this is an undesirable circumstance, it is represented with an error.
3 https://gabrieltanner.org/blog/GoLang-error-handling-definitive-guide
package main
import (
"fmt"
"ioutil"
)
func main() {
dir, err := ioutil.TempDir("", "temp")
if err != nil {
return fmt.Errorf("failed to create temp dir is: %v", err)
}
}
The built-in error type in GoLang is used to represent errors, which we shall
look at in more detail in the next section. As illustrated in the above
example, the error is frequently returned as a second parameter to the
function. The TempDir method returns the directory name and an error
variable in this case.
The interface only has one method, Error(), which returns an error message
as a string. Any type that implements the error interface can be utilized as
an error. When displaying the error with techniques such as fmt. GoLang
automatically calls the Error() function when it encounters an error.
In GoLang, there are several methods for constructing bespoke error
messages, each with its own set of benefits and drawbacks.
The errors.New() method is used to create new errors and takes the error
message as its only argument.
PathError implements the Error() method and hence conforms to the error
interface. Implementing the Error() method now returns a string containing
the PathError struct’s path. We may now use PathError to throw an error
anytime we wish.
Here is a simple example:
package main
import(
"fmt"
)
func main() {
err := throwError()
if err != nil {
fmt.Println(err)
}
}
func main() {
numb, err := divide(200, 0)
if err != nil {
fmt.Printf("error: %s", err.Error())
} else {
fmt.Println("Number: ", numb)
}
}
If the returned error is not nil, it is clear that there is a problem, and we need
to deal with it correctly. Depending on the situation, this may entail sending
a log message to the user, retrying the function until it works, or closing the
application entirely. The only downside is that GoLang does not need to
process returned errors, so we may just ignore handling errors.
Consider the following code as an example:
package main
import (
"errors"
"fmt"
)
func main() {
num2, _ := divide(100, 0)
fmt.Println("Number: ", num2)
}
Defer
A defer statement is a technique for deferring a function by placing it on an
executed stack after the function containing the defer statement has been
completed, either generally by executing a return statement or abnormally
by panicking. Deferred functions are then run in the reverse order that they
were deferred.
As an example, consider the following function:
When the function completes, all delayed functions are run in the opposite
order in which they were deferred.
package main
import (
"fmt"
)
func main() {
first()
}
func first() {
defer fmt.Println("first")
second()
}
func second() {
defer fmt.Println("second")
third()
}
func third() {
defer fmt.Println("third")
}
third
second
first
Panic
A panic statement informs GoLang that our code is unable to tackle the
present problem and, as a result, the usual execution flow of our code is
halted. When panic is triggered, all delayed functions are performed, and
the program crashes with a log message including the panic values
(typically an error message) and a stack trace.
GoLang, for example, will panic if an integer is split by zero.
package main
import "fmt"
func main() {
divide(5)
}
func divide(x int) {
fmt.Printf("divide(%d) \n", x+0/x)
divide(x-1)
}
When the division function is invoked with a value of zero, the program
panics, resulting in the results seen below.
We may also panic in our own applications by using the built-in panic
function. A panic should be used only when something unexpected occurs
that the application is unable to manage.
func getArguments() {
if len(os.Args) == 1 {
panic("Not enough-arguments!")
}
}
package main
import (
"fmt"
)
func main() {
accessSlice([]int{11,22,55,66,77,88}, 0)
}
func accessSlice(slice []int, index int) {
fmt.Printf("item %d, value %d \n", index, slice[index])
defer fmt.Printf("defer %d \n", index)
accessSlice(slice, index+1)
}
item 0, value 1
item 1, value 2
item 2, value 5
item 3, value 6
item 4, value 7
item 5, value 8
defer 5
defer 4
defer 3
defer 2
defer 1
defer 0
panic: runtime error: index out of range [6] with length 6
goroutine 1 [running]:
main.accessSlice(0xc00011df48, 0x6, 0x6, 0x6)
C:/Users/gabriel/articles/GoLang Error
handling/Code/panic/main.go:29 +0x250
main.accessSlice(0xc00011df48, 0x6, 0x6, 0x5)
C:/Users/gabriel/articles/GoLang Error
handling/Code/panic/main.go:31 +0x1eb
main.accessSlice(0xc00011df48, 0x6, 0x6, 0x4)
C:/Users/gabriel/articles/GoLang Error
handling/Code/panic/main.go:31 +0x1eb
main.accessSlice(0xc00011df48, 0x6, 0x6, 0x3)
C:/Users/gabriel/articles/GoLang Error
handling/Code/panic/main.go:31 +0x1eb
main.accessSlice(0xc00011df48, 0x6, 0x6, 0x2)
C:/Users/gabriel/articles/GoLang Error
handling/Code/panic/main.go:31 +0x1eb
main.accessSlice(0xc00011df48, 0x6, 0x6, 0x1)
C:/Users/gabriel/articles/GoLang Error
handling/Code/panic/main.go:31 +0x1eb
main.accessSlice(0xc00011df48, 0x6, 0x6, 0x0)
C:/Users/gabriel/articles/GoLang Error
handling/Code/panic/main.go:31 +0x1eb
main.main()
C:/Users/gabriel/articles/GoLang Error
handling/Code/panic/main.go:9 +0x99
exit status 2
Recover
Panics should not terminate the program in some uncommon instances, but
should instead be recovered. For example, if a socket server experiences an
unexpected difficulty, it should notify the clients and subsequently
disconnect all connections, rather than keeping the clients in the dark about
what happened.
Recover panics by executing the built-in recover function within a
delayed function within the panicking function. The current state of panic
will then end, and the panic error value is returned.
package main
import "fmt"
func main(){
accessSlice([]int{1,2,5,6,7,8}, 0)
}
func accessSlice(slice []int, index int) {
defer func() {
if p := recover(); p != nil {
fmt.Printf("internal error: %v", p)
}
}()
fmt.Printf("item %d, value %d \n", index, slice[index])
defer fmt.Printf("defer %d \n", index)
accessSlice(slice, index+1)
}
item 0, value 1
item 1, value 2
item 2, value 5
item 3, value 6
item 4, value 7
item 5, value 8
internal error: runtime error: index out of range [6] with length
6defer 5
defer 4
defer 3
defer 2
defer 1
defer 0
Recovering from panic attacks might be beneficial in specific situations, but
we should avoid recovering from panic attacks as a general rule.
Error Wrapping
GoLang also allows errors to wrap up other errors, allowing us to provide
additional context to our error messages. This is frequently used to offer
complete details, such as where the issue occurred in our software.
As seen in the following example, we may produce wrapped errors by
using the percent w (%w) option with the fmt.Errorf function.
package main
import (
"errors"
"fmt"
"os"
)
func main() {
err := openFile("non-existing")
As we can see, the program outputs both the new error message generated
by fmt.Errorf and the old error message supplied to the %w flag. GoLang
also can recover a previous error message by unwrapping the error using
errors.Unwrap.
package main
import (
"errors"
"fmt"
"os"
)
func main() {
err := openFile("nonexisting")
if err != nil {
fmt.Printf("error running-program: %s \n", err.Error())
// Unwrap error
unwrappedErr := errors.Unwrap(err)
fmt.Printf("unwrapped-error: %v \n", unwrappedErr)
}
}
Casting Errors
We’ll need the means to cast between different error types from time to
time, for example, to access unique information that only that type holds.
The “errors.As” function makes this simple and safe by looking for the first
error in the error chain that meets the error type’s specifications. If there is
no match, the method here returns false.
Let’s have a look at the official errors.As To further understand what is
going on, consider using the documentation example.
package main
import (
"errors"
"fmt"
"io/fs"
"os"
)
func main(){
// Casting error
if _, err := os.Open("non-existing"); err != nil {
var pathError *os.PathError
if errors.As(err, &pathError) {
fmt.Println("Failed at path:", pathError.Path)
} else {
fmt.Println(err)
}
}
}
Here, we attempt to cast our general error type to os.PathError to access the
Path variable included in that specific issue.
Another essential feature is determining if a mistake is of a certain type.
To do this, GoLang provides the “errors.Is” function. This section specifies
our error and the specific error type we wish to verify. If the error is correct,
the function will return true; otherwise, it will return false.
package main
import (
"errors"
"fmt"
"io/fs"
"os"
)
func main(){
// Check if error is specific type
if _, err := os.Open("non-existing"); err != nil {
if errors.Is(err, fs.ErrNotExist) {
fmt.Println("file doesn't exist")
} else {
fmt.Println(err)
}
}
}
Beginning
Unlike most other languages I’ve dealt with, Go includes a built-in test
runner and a foundation for standard language tools. The simplest approach
to run unit tests for your project is from the command line using a
command like go test. /… This program will find and execute any tests in
your current directory or its subdirectories.
Tests must be written in files distinct from the main package code and
must finish with the suffix_test.go. Starting with func Test identifies a test
function. One of the most basic tests might look like this:
import (
"fmt"
"testing"
"github.com/PullRequestInc/pkg/hello"
)
Unique to Go
If we’re used to creating unit tests in other languages, there are a few
differences in writing unit tests in Go.
For one thing, we’ll notice in the above example that the testing appears
“crude,” We execute the check ourselves and then return an error message.
There are no helpers built-in for stuff like t.assertEquals (expected, actual).
This is because Go errs on the side of minimalism, giving only the
essentials required to do a task in one method. This allows the language to
preserve backward compatibility for an extended time while also focusing
on optimizing and enhancing these basic functions over time.
That being said, testify is a fantastic third-party package that is frequently
used for executing various sorts of assertions. Testify includes the assert and
need packages – the assert, like the built-in t.Fail will fail the test on the
first failure it sees. Whereas t.Error will just report an error and continue the
test; need will simply report an error and continue the test. We prefer using
the assert package and believe it complements the built-in Go test tooling
and infrastructure well. The fundamental structure of your tests will remain
the same, except for certain assertion helpers that will result in significantly
fewer lines of code. The above example would become:
// hello_test1.go
package hello_test1
import (
"fmt"
"testing"
"github.com/PullRequestInc/pkg/hello"
)
func TestHello(t *testing.T) {
type test struct {
input string
expected string
}
tests := []test{
{input: "world", expected: "hello everyone"},
{input: "pullrequest", expected: "hello pullrequest"},
}
This is just a simple example, and I’m not really utilizing the setUp or
tearDown functions, but we can see where we can use such chores. On the
other hand, these table-driven tests may be as complex as we want them to
be. We may wish to pass a function as a parameter for some test scenarios
and call distinct functions for tests with a shared setUp and tearDown.
package hello
"on_save": [{
"cmd": "gs9o_run_many", "args": {
"commands":[
["clear"],
["sh", "if [ -f onsave.sh ]; then. /onsave.sh; else gofmt -
s -w. / && go build. errors && go test -i && go test &&
go vet && golint ; fi"]
],
"focus_view": false
}
}],
"fmt_cmd": ["goimports"]
package mailman
import "net/mail"
type MailMan struct{}
func (ml *MailMan) Send(subject, body string, to
...*mail.Address) {
// some code
}
func New() *MailMan {
return &MailMan{}
}
If the method under test requires a “MailMan” object, our test code can
only call it by giving an actual “MailMan” instance.
A genuine email might be sent every time we run our tests. Now think
about it what would happen if we used the above-mentioned on-save
functionality. We’d rapidly irritate our test users or rack up large service
costs.
Another option is to incorporate the following primary interface into our
code:
In our test code, we may use a bogus sender and make assertions on the
fields after calling the target function:
This chapter discussed refactoring, error handling, and writing tests with
relevant examples.
chapter 3
Packaging in GoLang
DOI: 10.1201/9781003309055-3
IN THIS CHAPTER
PACKAGES IN GoLang
In this session, we’ll look at packages in the Go programming language.
When designing applications, it is vital to write maintainable and reusable
code. Go provides modularity and code reusability through its package
ecosystem. Go pushes us to package little bits of software and then utilize
these small packages to assemble larger applications.
Workspace
While we go into Go packages, let’s discuss Workspace structure code. Go
programs are kept in a directory structure known as a workspace. A
workspace is just the home directory for our Go programs. There are three
subdirectories at the root of a workspace:
Main Package
When we create reusable code, you will create a package as a shared
library. However, while creating executable applications, we will utilize the
package “main” to convert the package into an executable program. The
package “main” instructs the Go compiler to construct the package as an
executable application rather than a shared library. The main function in
package “main” will serve as the executable program’s entry point. When
you create shared libraries, there will be no main package or main function
in the package.
Here’s an example executable program that uses the package main, with
the function main serving as the entry point.
package main
import (
"fmt"
)
func main(){
fmt.Println("Hello, Everyone")
}
Import Packages
When importing a package into another package, “import” is used. We
imported the package “fmt” into the sample program in Code to use the
method Println. The “fmt” package is part of the Go standard library. When
we import packages, the Go compiler searches for them in the locations
indicated by the environment variables GOROOT and GOPATH. The
GOROOT directory contains packages from the standard library. The
GOPATH location contains packages that we have written and third-party
packages that we have imported.
go get gopkg.in/mgo.v2
After installing the mgo, add the following import statement to our apps to
reuse the code:
import (
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
The MongoDB driver, mgo, provides two packages we have imported in the
preceding import statement.
Init Function
When writing Go packages, we may include a function called “init” that is
called at the start of the execution period. The init function is useful for
adding initialization logic into a package.
package db
import (
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
func init {
// here initialization-code
}
package main
import (
_ "mywebapp/libs/mongodb/db"
"fmt"
"log"
)
func main() {
//implementation-here
}
We’re importing two separate packages from two different locations, but
their names are identical. We may create an alias name for a single package
and use it anytime we need to invoke a method in that package.
Important Considerations
1. Import paths: In the Go programming language, each package is
specified by a unique string called an import path. We may
import packages into our program using an import route. As an
example:
import "fmt"
package main
import _ "strings"
The math package is the primary package in this case, while the
cmplx package is the nested package.
6. Although some packages may have the same name, the route to
such packages is always distinct. For example, both the math and
crypto packages have a rand-named package, but their paths are
different, i.e., math/rand and crypto/rand.
7. In Go programming, why is the main package usually at the top
of the program? Because the main package instructs the go build
that the linker must enable to create an executable file.
Code Exported
We might have noticed the declarations in the greet.go file we called were
all uppercase. Go, unlike other languages, does not have the idea of public,
private, or protected modifiers. Capitalization governs external visibility.
Types, variables, functions, and that begin with a capital letter are publicly
accessible outside of the current package. A symbol that can be seen outside
of its container is termed exported.
If we add a new reset method to Octopus, we may call it from the
welcome package but not from our main.go file, which is not part of the
greet package:
package greet
import "fmt"
var Shark = "Rammy"
type Octopus struct {
Name string
Color string
}
func (o Octopus) String() string {
return fmt.Sprintf("Octopus's name is %q and the
color %s.", o.Name, o.Color)
}
func (o *Octopus) reset() {
o.Name = ""
o.Color = ""
}
func Hello() {
fmt.Println("Hello, Everyone")
}
package greet
import "fmt"
var Shark = "Rammy"
type Octopus struct {
Name string
Color string
}
func (o Octopus) String() string {
return fmt.Sprintf("The octopus's name is %q and
is the color %s.", o.Name, o.Color)
}
func (o *Octopus) Reset() {
o.Name = ""
o.Color = ""
}
func Hello() {
fmt.Println("Hello, Everyone")
}
package main
import (
"fmt"
"github.com/gopherguides/greet"
)
func main() {
greet.Hello()
fmt.Println(greet.Shark)
oct := greet.Octopus{
Name: "Tessa",
Color: "White",
}
fmt.Println(oct.String())
oct.Reset()
fmt.Println(oct.String())
}
Awesome Go
Before we get into the libraries, we’d like to introduce you to Awesome Go,
a constantly updated and selected collection of Go libraries and other
resources. We should check in every now and then to see what’s new.
GoLang-Set
Go contains arrays, slices, and maps but no fixed data structure. Use a map
of bools to simulate a set, but it’s preferable to have a real data type with the
appropriate methods and semantics. This is where GoLang-set comes in.
Here’s a simple example of establishing a new set, adding items, and testing
membership:
package main
import (
"fmt"
"github.com/deckarep/GoLang-set"
)
func main() {
basicColors := mapset.NewSet()
basicColors.Add("Yellow")
basicColors.Add("White")
basicColors.Add("Grey")
if basicColors.Contains("Grey") {
fmt.Println("Yay! 'Grey' is a basic color")
} else {
fmt.Println("What a disappointment! 'Grey' is not a basic
color")
}
if basicColors.Contains("Pink") {
fmt.Println("Yay! 'Pink' is a basic color")
} else {
fmt.Println("What a disappointment! 'Pink' is not a basic
color")
}
}
Output:
Yay! 'Grey' is a basic color
What a disappointment! 'Pink' is not a basic color.
It is worth noting that the package name is “mapset.” Aside from the
fundamentals, we may execute all set operations such as union, intersection,
and difference. We may also iterate through the values we’ve chosen:
package main
import (
"fmt"
"github.com/deckarep/GoLang-set"
)
func main() {
basicColors := mapset.NewSet()
basicColors.Add("White")
basicColors.Add("Brown")
basicColors.Add("Grey")
otherColors := mapset.NewSetFromSlice([]interface{}
{"Pink", "Black", "Indigo", "Blue"})
rainbowColors := basicColors.Union(otherColors)
for color := range rainbowColors.Iterator().C {
fmt.Println(color)
}
}
Color
Let’s keep the color scheme going. When designing command-line
applications, use colors to emphasize important messages or distinguish
between faults, successes, and warnings.
The color package allows you to add color to our applications easily. It
employs ANSII escape codes and is also compatible with Windows! Here’s
a simple example:
package main
import (
"github.com/fatih/color"
)
func main() {
color.Red("Carrot is Orange ")
color.Blue("Sky is blue")
}
The color package allows us to blend colors with backdrop colors, use
styles like strong or italic, and splatter color over non-color output.
package main
import (
"github.com/fatih/color"
"fmt"
)
func main() {
minion :=
color.New(color.FgBlack).Add(color.BgYellow).Add(color.Bol
d)
minion.Println("Minion says: banana!")
m := minion.PrintlnFunc()
m("We want another banana!")
slantedRed := color.New(color.FgRed, color.BgWhite,
color.Italic).SprintFunc()
fmt.Println("We've made a huge", slantedRed("mistake"))
}
Now
Now is a very basic package that acts as a wrapper for the standard time
package, making it easy to interact with the various date and time
constructions centered around the current time.
We can, for example, retrieve the start of the current minute or the end of
the Sunday closest to the present time. Here’s an example of how to utilize
“now”:
package main
import (
"github.com/jinzhu/now"
"fmt"
)
func main() {
fmt.Println("All beginnings….")
fmt.Println(now.BeginningOfMinute())
fmt.Println(now.BeginningOfHour())
fmt.Println(now.BeginningOfDay())
fmt.Println(now.BeginningOfWeek())
fmt.Println(now.BeginningOfMonth())
fmt.Println(now.BeginningOfQuarter())
fmt.Println(now.BeginningOfYear())
}
Output:
All the beginnings...
2017-06-04 16:59:00 -0700 PDT
2017-06-04 16:00:00 -0700 PDT
2017-06-04 00:00:00 -0700 PDT
2017-06-04 00:00:00 -0700 PDT
2017-06-01 00:00:00 -0700 PDT
2017-04-01 00:00:00 -0700 PDT
2016-12-31 23:00:00 -0800 PST
We may also parse dates and timings and even create our own forms (which
will require updating the known formats). The Now type incorporates time.
We have time so that you can utilize it all. Apply time methods directly to
now objects.
Gen
The gen tool creates code for you, specifically type-aware code that
attempts to bridge the gap left by Go’s lack of templates and generics.
We add a specific comment to our types, and gen creates source files to
include in our project. There is no runtime magic. Let’s look at the
following example, which is of the annotated type.
// +gen slice:"Where,Count,GroupBy[int]"
type Person struct {
Names string
Ages int
}
The code includes LINQ-like methods for working with the PersonSlice
type. It’s straightforward to grasp and well-documented.
Here’s how we put it to use. A PersonSlice is defined in the main
function. The age() function chooses the age field from the Person
parameter. The produced GroupByInt() method takes the age() function and
groups the persons in the slice by age.
package main
import (
"fmt"
)
// +gen slice:"Where,Count,GroupBy[int]"
type Person struct {
Name string
Age int
}
func age(p Person) int {
return p.Age
}
func main() {
people := PersonSlice {
{"Jimy", 44},
{"Hane", 28},
{"Lyle", 21},
}
groupedByAge := people.GroupByInt(age)
fmt.Println(groupedByAge)
}
Output:
map[44:[{Jimy 44}] 28:[{Hane 28} {Lyle 21}]]
Gorm
Go is noted for its austerity. Database programming is no exception. The
majority of popular Go DB libraries are rather low-level. Gorm introduces
the realm of object-relational mapping to Go by including the following
features:
package main
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type Product struct {
gorm.Model
Code string
Price uint
}
func main() {
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
panic("failed to connect the database")
}
defer db.Close()
// Migrate schema
db.AutoMigrate(&Product{})
// Create
db.Create(&Product{Code: "L1212", Price: 1200})
// Read
var product Product
db.First(&product, 1) // find product with the id 1
db.First(&product, "code =? ", "L1212")
// Update – update-product's price to 2200
db.Model(&product).Update("Price", 2200)
// Delete – delete-product
db.Delete(&product)
Goose
Managing the schema is one of the most critical jobs when dealing with
relational databases. Changing the database schema is seen as a
“dangerous” modification in certain businesses. If necessary, the goose
package allows us to perform schema modifications and even data
migrations. We may go back and forth by goose up and goose down. But
keep an eye on our data and make sure it doesn’t get lost or damaged.
Goose works by versioning our schema and utilizing migration files for
each schema. SQL commands or Go commands are used in the migration
files. An example of a SQL migration file that adds a new table is shown
below:
-- +goose Up
CREATE TABLE person (
id int NOT NULL,
names text,
ages int,
PRIMARY KEY(id)
);
-- +goose Down
DROP TABLE person;
Glide
Glide is a Go package manager. Many programs with conflicting
dependencies may run under a single GOPATH. The answer is for each
application to keep track of its own vendor directory of package
dependencies. Glide assists with this task.
Glide has the following characteristics:
Ginkgo
Ginkgo is a testing framework for Behavior Driven Development (BDD). It
allows us to write your tests in a syntax similar to English, allowing less
technical individuals to analyze the tests and ensure that they meet the
business requirements.
This form of a test specification is also popular among developers. It
connects with Go’s built-in testing package and is frequently used in
conjunction with Gomega. An example of a Ginkgo + Gomega test is as
follows:
Etcd
Etcd is a trustworthy distributed Key-Value store. The server is written in
Go, and the Go client communicates with it through gRPC.
It focuses on the following aspects:
func test_get() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: endpoints,
DialTimeout: dialTimeout,
})
if err != nil {
log.Fatal(err)
}
defer cli.Close()
NSQ
NSQ is a fantastic distributed queue. It has served me well as the main
building component for large-scale distributed systems. Here are some of its
characteristics:
package main
import (
"github.com/bitly/go-nsq"
)
func main() {
config := nsq.NewConfig()
p, _ := nsq.NewProducer("127.0.0.1:4150", config)
p.Publish("topic", []byte("messages"))
p.Stop()
}
Docker
Docker is now a household name. We might be surprised to learn that
Docker is built into Go. We don’t usually use Docker in our work, but it’s
an important project that deserves to be acknowledged as a very successful
and popular Go project.
Kubernetes
Kubernetes is a cloud-native container orchestration tool that is free source.
It is another massively distributed system written in Go. We recently
published a book, called Mastering Kubernetes, in which we go into great
detail about the most advanced features of Kubernetes. Kubernetes is
incredibly versatile from the standpoint of a Go developer, and it can be
extended and customized via plugins.
DEPENDENCY MANAGEMENT
Go employs a unique approach to dependency management in that it is
source-based rather than artifact-based. Packages in an artifact-based
dependency management system are made up of artifacts created from
source code and are kept in a repository system distinct from the source
code. Many NodeJS packages, for example, utilize npmjs.org as a package
repository and github.com as a source repository. Similarly, on the other
hand, packages in Go are source code, and publishing a package does not
require the creation of artifacts or establishing a separate repository. Go
packages must be kept in a VCS server’s version control repository.
Dependencies are retrieved either directly from their VCS server or through
an intermediate proxy, which retrieves them from their VCS server.
Versioning
Modules and first-class package versioning are added to the Go ecosystem
with versioning Go 1.11. Previously, Go lacked a well-defined system for
version control. While third-party version management solutions were
available, the native Go experience did not allow versioning.
Semantic versioning is used in Go Modules. A module’s versions are
specified as version control system (VCS) tags that represent legitimate
semantic versions prefixed with v. To release version 1.0.0 of
gitlab.com/my/project, for example, the developer must generate the Git tag
v1.0.0.
Other than 0 and 1, the module name is prefixed with/vX, where X is the
major version. For example, gitlab.com/my/project/v2.0.0 must be titled
and imported as gitlab.com/my/project/v2.1
1 https://docs.gitlab.com/ee/development/go_guide/dependencies.html
Modules
In GoLang, a module is a grouping of related packages versioned as one
unit. We may use Go Modules to implement specific dependency needs and
create repeatable builds for multiple environments. It dramatically
simplifies dependency management in GoLang.
To begin utilizing Modules in your project, just run the following
command (this is relevant if our project currently uses version control):
go mod init
When we run the above command, it will create a go.mod module config
file in the root directory of our project. The module and Go version
information is included in the file.
module github.com/Alfrick/Go-Test
go 1.14
The file specifies the project’s needs and includes all required
dependencies, similar to the “package.json” file used in “Node.js”
dependency management.
Installing Dependencies
We may now incorporate new dependencies into our codebase after starting
our project to begin utilizing Go Modules.
To install dependencies in GoLang, use the go get command,
automatically updating the go.mod file.
Here’s an illustration:
go get github.com/lib/pq
go get github.com/lib/pq@master
Alternatively, here’s a more specific version:
go get github.com/lib/[email protected]
After we execute the command, our go.mod file should look like this:
module github.com/Alfrick/Go-Test
go 1.14
require github.com/lib/pq v1.8.0 // indirect
// main.go
package main
import (
"github.com/lib/pq"
)
func main() {
pq.Load()
}
go mod tidy
// main.go
package main
import (
"github.com/lib/pq"
"github.com/subosito/gotenv"
)
func main() {
pq.Load()
log.Println(os.Getenv("APP_ID"))
}
The program will automatically retrieve the missing package and add it to
our go.mod file before our project is constructed when we run go build.
The modified go.mod file is available here:
module github.com/Alfrick/Go-Test
go 1.14
require (
github.com/lib/pq v1.8.0
github.com/subosito/gotenv v1.2.0
)
go get github.com/lib/pq
go get github.com/lib/[email protected]
go get -u github.com/lib/pq
Listing Dependencies
Run the below-given command to see a list of the current module (also
known as the main module) and all its dependencies:
go list -m all
Naming
Except for the standard library, the name of a module or package must be of
the form (sub.)*domain.tld(/path)*. This is comparable to, but not the same
as, a URL. The package name lacks a scheme (such as https://) and cannot
include a port number. The name example.com:8443/my/package is invalid.
Fetching Packages
The following was the procedure for getting a package:
DOI: 10.1201/9781003309055-4
IN THIS CHAPTER
➢ Concurrency or parallelism
➢ Goroutines
➢ Shared resources
CONCURRENCY
Concurrency refers to a program’s capacity to do numerous tasks
simultaneously. This refers to a program with two or more jobs that
simultaneously execute independently but are still part of the same
program. Concurrency is critical in modern software because it allows
separate sections of code to be executed as quickly as feasible without
disrupting the overall flow of the program.
In GoLang language, concurrency refers to the ability for functions to
operate independently of one another. A goroutine is a function that may
execute in parallel with other functions. When you define a function as a
goroutine, it is viewed as an independent unit of scheduled work and then
runs on an available logical processor. The GoLang runtime scheduler
handles all goroutines that are formed and need processor time. To run
goroutines, the scheduler ties operating system threads to logical
processors. Scheduler handles everything related to which goroutines are
executing on which logical processors at any one time by sitting on top of
the operating system.
Threads are used to provide concurrency in popular programming
languages, such as Java and Python. Concurrency mechanisms provided
into GoLang include goroutines and channels. Concurrency is cheap and
simple in GoLang. Goroutines are lightweight, low-cost threads. Channels
are the conduits that allow goroutines to communicate with one another.
Communicating Sequential Processes (CSP) is a term used to explain
how systems having numerous concurrent models should communicate
with one another. It is the underlying ethos of GoLang, and it often depends
heavily on utilizing channels as a mechanism for sending messages between
two or more concurrent processes.
Concurrency and parallelism come into play when looking for multitasking,
and they are frequently used interchangeably; concurrent and parallel refer
to similar but distinct aspects.
Concurrency is the ability to manage many tasks at the same time. This
suggests we’re attempting to manage many jobs at once in a short amount
of time. We will, however, only be executing one activity at a time. This is
common in programs where one job is waiting, and the software decides to
drive another task during the idle period. It is a feature of the issue domain
in which our application must handle several concurrent events.
Parallelism is the practice of performing many things at the same time.
This implies that even if we have two processes running concurrently, there
are no gaps in between. It is a feature of the solution domain in which we
wish to speed up our program by processing separate parts of the issue in
parallel.
A concurrent program contains many logical control threads. These
threads may or may not run concurrently. A parallel program might
theoretically run faster than a sequential program since it does various
sections of the calculation simultaneously (in parallel). It may or may not
contain more than one logical control thread.
package main
import (
"fmt"
"sync"
"time"
)
const (
sleeping = iota
checking
cutting
)
// Barber-goroutine
// Checks for the customers
// Sleeps - wait for the wakers to wake him up
func barber(b1 *Barber, wr chan *Customer, wakers chan
*Customer) {
for {
b1.Lock()
defer b1.Unlock()
b1.state = checking
b1.customer = nil
// customer-goroutine
// just fizzles out if it's full, otherwise customer
// is passed along to channel handling it's haircut etc
func customer(c1 *Customer, b1 *Barber, wr chan<-
*Customer, wakers chan<- *Customer) {
// arrive
time.Sleep(time.Millisecond * 50)
// Check on barber
B1.Lock()
fmt.Printf("The Customer %s checks %s barber | room: %d,
w %d - customer: %s\n",
c1, stateLog[b1.state], len(wr), len(wakers), b1.customer)
switch b1.state {
case sleeping:
select {
case wakers <- c1:
default:
select {
case wr <- c1:
default:
wg.Done()
}
}
case cutting:
select {
case wr <- c1:
default: // Full waiting room, and leave shop
wg.Done()
}
case checking:
panic("Customer shouldn't check for Barber when Barber is
Checking the waiting room")
}
b1.Unlock()
}
func main() {
b1 := NewBarber()
b1.name = "Jocky"
WaitingRoom := make(chan *Customer, 5) // 5-chairs
Wakers := make(chan *Customer, 1) // Only-one waker at
time
go barber(b1, WaitingRoom, Wakers)
time.Sleep(time.Millisecond * 100)
wg = new(sync.WaitGroup)
n := 10
wg.Add(10)
// Spawn customers
for i := 0; i < n; i++ {
time.Sleep(time.Millisecond * 50)
c1 := new(Customer)
go customer(c1, b1, WaitingRoom, Wakers)
}
wg.Wait()
fmt.Println("No more customers for day")
}
package main
import (
"flag"
"fmt"
"log"
"os"
"runtime"
"runtime/pprof"
)
type Consumer struct {
msgs *chan int
}
// Producer-definition
type Producer struct {
msgs *chan int
done *chan bool
}
func main() {
// profile flags
cpuprofile := flag.String("cpuprofile", "", "write cpu profile
to 'file'")
memprofile := flag.String("memprofile", "", "write memory
profile to 'file'")
// get the maximum number of messages from the flags
max := flag.Int("n", 5, "defines number of messages")
flag.Parse()
// CPU Profile
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("couldn't create CPU profile: ", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("couldn't start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
var msgs = make(chan int) // channel for message
transmission
var done = make(chan bool) // channel for controlling when
production is completed
// Memory Profile
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal("couldn't create memory profile: ", err)
}
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
}
f.Close()
}
}
Go Concurrent Programming
Concurrent programming uses the many processor cores seen in most
modern computers. The idea has been around for a long time, even when
the single Core had one core. Many computer languages, including C/C++,
Java, and others, use many threads to achieve some concurrency.
A single thread is a small set of instructions scheduled to be executed
sequentially. We may think of it as a task within a larger enterprise.
func name(){
// statement
}
// using the go keyword as the prefix of a function
call
go name()
Example:
// Program to illustrate the concept of Goroutine
package main
import "fmt"
func display(str string) {
for g := 0; g < 5; g++ {
fmt.Println(str)
}
}
func main() {
// Calling-Goroutine
go display("Welcomehome")
// Calling-normal-function
display("Helloeveryone")
}
MULTIPLE Goroutines
In our program, a Goroutine is a function or procedure that runs
independently and simultaneously with other Goroutines. In other terms, a
Goroutine is any continuously running activity in the Go programming
language. We may have several goroutines in a single program in the Go
computer language. We may create a goroutine by prefixing the function or
method call with the go keyword, as seen below:
func name(){
// statement(s)
}
// using the go keyword as the prefix of your function
call
go name()
Example:
// Program to demonstrate the Multiple Goroutines
package main
import (
"fmt"
"time"
)
// For goroutine 1
func Aname() {
arr1 := [4]string{"Shreya", "Disha", "Kartik",
"Mira"}
for t1 := 0; t1 <= 3; t1++ {
time.Sleep(160 * time.Millisecond)
fmt.Printf("%s\n", arr1[t1])
}
}
// For goroutine 2
func Aid() {
arr2 := [4]int{500, 102, 209, 901}
for t2 := 0; t2 <= 3; t2++ {
time.Sleep(500 * time.Millisecond)
fmt.Printf("%d\n", arr2[tk2])
}
}
// main-function
func main() {
fmt.Println("!Main Go-routine Start!")
// calling-Goroutine 1
go Aname()
// calling-Goroutine 2
go Aid()
time.Sleep(3100 * time.Millisecond)
fmt.Println("\n!Main Go-routine End!")
}
Sr.
No. Concurrency Parallelism
Concurrency is the problem of Parallelism is the job of
simultaneously conducting and conducting numerous
1.
managing numerous calculations at the same
computations. time.
Concurrency is achieved by the
While it is accomplished
interleaving of processes on the
through the use of several
2. central processing unit (CPU),
central processing units
also known as context
(CPUs).
switching.
While this is not
Concurrency can be accomplished with a single
3. accomplished by utilizing a processing unit, it
single processing unit. necessitates the use of
numerous processing units.
Concurrency increases the While increasing the
4. quantity of work that is system’s throughput and
completed simultaneously. processing performance.
Concurrency deals with many While it does several things
5.
things at the same time. at the same time.
The non-deterministic control
While the method is
6. flow technique is referred to as
deterministic control flow.
concurrency.
While debugging is difficult,
Debugging with concurrency is
7. it is simpler than
difficult.
concurrency.
// poll URL
// update Resource's polling and lastPolled
res.lock.Lock()
r1.polling = false
r1.lastPolled = time.Nanoseconds()
res.lock.Unlock()
}
}
The delicate logic from the previous example has vanished, and our
Resource data structure no longer holds accounting data. In reality, all that’s
left are the essentials. This should give us an idea of how powerful these
simple language features are.
HOW TO USE Goroutines TO ACCESS SHARED RESOURCES
Access Shared Resources Using Goroutines
In the previous section, we discussed what Goroutines are and how to
utilize them in our project. However, we have not discussed any issues that
may arise while utilizing Goroutines.
We know that Goroutines let the program operate concurrently, but we
must utilize them with caution when accessing specific shared resources.
Assume one go thread is running to credit the amount to the account
while another go thread is running in parallel to debit the amount from the
account.
We have no control over how each Goroutine is executed. As a result,
one thread might be performed instantly while another can be internally
delayed.
So, suppose Thread 1 credits $100 to X account and Thread 2 debits
$100 from X account, then what happens is they both access the identical
starting value, which is assumed to be $500.
If Thread 2 is processed immediately and returns the current amount to
$400, Thread 1 retains the reference to an initial value of $500 and executes
on top of that, returning $600 to the account.
However, the real amount must be $500 since the $100 will be credited,
and the $100 will be debited from the same account. Therefore, the total
must be $500. This results in inconsistency while working with
concurrency.
So, when one thread accesses a specific variable, all other threads should
be blocked from altering or accessing the same resource.
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func credit() {
for c := 0; c < 5; c++ {
mutex.Lock()
balance += 120
time.Sleep(time.Duration(rand.Intn(100)) *
time.Millisecond)
fmt.Println("After crediting, balance:", balance)
mutex.Unlock()
}
}
func debit() {
for c := 0; c < 5; c++ {
mutex.Lock()
balance -= 120
time.Sleep(time.Duration(rand.Intn(100)) *
time.Millisecond)
fmt.Println("After debiting, balance:", balance)
mutex.Unlock()
}
}
func main() {
balance = 220
fmt.Println("Initial balance:", balance)
go credit()
go debit()
fmt.Scanln()
}
The Mutex object’s Lock() and Unlock() procedures allow you to mark the
beginning and conclusion of a vital section and prevent several goroutines
from accessing the same resource. So, while one Goroutine has the lock, the
other Goroutine has no choice except to wait till Unlock() is called.
import (
"fmt"
"math/rand"
"sync"
"sync/atomic"
"time"
)
func credit() {
for c := 0; c < 10; c++ {
// adds 120 to balance atomically
atomic.AddInt64(&balance, 100)
time.Sleep(time.Duration(rand.Intn(120)) *
time.Millisecond)
}
}
func debit() {
for c := 0; c < 5; c++ {
// deducts -120 from balance atomically
atomic.AddInt64(&balance, -120)
time.Sleep(time.Duration(rand.Intn(120)) *
time.Millisecond)
}
}
func main() {
balance = 220
fmt.Println("Initial balance:", balance)
go credit()
go debit()
fmt.Scanln()
Concurrency Issues
Non-atomic: Non-atomic operations that are interruptible by several
processes might cause issues.
Non-atomic: Non-atomic operations that are interruptible by several
processes might cause issues.
Race conditions: Race circumstances occur when the result is
determined by who of multiple processes arrives at a place first.
Blocking: Processes might become stuck while waiting for resources.
A procedure might be stalled for an extended time while waiting for
input from a terminal. This would be highly undesirable if the
procedure is needed to update specific data regularly.
Starvation: Starvation happens when a process cannot get service to
progress.
Deadlock: Deadlock happens when two processes are stalled, and
neither can continue to operate.
Concurrency Disadvantages
It is necessary to secure several apps from one another.
It is mandatory to use extra techniques to coordinate several apps.
Switching between apps necessitates additional performance
overheads and complexity in operating systems.
Running too many programs concurrently might result in substantially
decreased performance.
Benefits of Concurrency
Many programs are running: It allows us to run multiple apps
simultaneously.
Improved resource usage: It allows resources that are not being used
by one program to be utilized by another.
Improved average response time: Without concurrency, each
application is completed before starting the next one.
Better performance: It helps the operating system to perform better.
When one application utilizes the CPU and another only uses the disk
drive, the time it takes to complete both applications simultaneously is
less than the time it takes to run each application sequentially.
DOI: 10.1201/9781003309055-5
IN THIS CHAPTER
➢ Flow control
➢ Types
➢ Modules and packages
➢ Error handling
➢ Comparing keywords and syntax
➢ Functions
FLOW CONTROL
There is just one loop in the Go programming language, and it is a for loop.
A for loop in GoLang is a type of repetition control structure that enables us
to create a loop that will run a set number of times. In the Go programming
language, this for loop is used in a variety of ways, including:
Syntax:
Here,
The initialization statement is optional and is executed before
the for loop starts. An introductory statement, such as variable
declarations, increment or assignment instructions, or function
calls, always includes an initialization statement.
The condition statement includes a boolean expression
evaluated at the beginning of each loop iteration. If the
conditional statement’s value is true, the loop runs.
The post statement follows the for loop body. The condition
statement is re-evaluated after the post statement; if the
conditional statement’s value is false, the loop is ended.
Example:
Syntax:
for
{
// Statements
}
Example:
// Program to show the use of an infinite loop
package main
import "fmt"
// main-function
func main() {
// infinite-loop
for {
fmt.Printf("Hello-everyone\n")
}
}
3. while for loop: A while loop can be used in place of a for loop.
This loop is performed until the condition stated is met. When the
value of the specified condition is false, the loop is ended.
Syntax:
for condition{
// statements
}
Long Description
While loop in GoLang.
Example:
4. Simple range in for loop: The range can also be used to create a
loop.
Syntax:
Example:
5. Using a for loop for strings: A for loop can run over the
Unicode code points in a text.
Syntax:
Example:
Syntax:
Example:
7. For channel: A for loop can cycle over the channel’s successive
data until the channel is terminated.
Syntax:
for item := range Chnl {
// statement..
}
Example:
// A code to demonstrate the use of a loop
using channel
package main
import "fmt"
// main-function
func main() {
// using-channel
chnl := make(chan int)
go func(){
chnl <- 200
chnl <- 2000
chnl <- 20000
chnl <- 200000
close(chnl)
}()
for d:= range chnl {
fmt.Println(d)
}
}
Break
Goto
Continue
Break Statement
The break statement is used to end the loop or statement it presents. If they
are present, the control is then passed to the statements that follow the break
statement. If the break statement is included in the nested loop, it only stops
the loops that contain the break statement.
// Go program to show the
// use of break statement
package main
import "fmt"
// Main function
func main() {
for c:=0; c<5; c++{
fmt.Println(c)
// For loop breaks when the value of c = 4
if c == 4{
break;
}
}
}
Goto Statement
This statement is used in the program to transfer control to the labeled
statement. The label serves as valid identification and is inserted
immediately before the statement from which control is transferred.
Programmers rarely use Goto statements because they make tracking the
program’s control flow harder.
Continue Statement
This statement is used to ignore the execution portion of the loop if a given
condition is met. The control is then returned to the beginning of the loop. It
skips the following statements and proceeds with the next loop iteration.
// Go program to illustrate
// the use of continue statement
package main
import "fmt"
func main() {
var y int = 0
// for loop work as a while loop
for y < 9 {
if y == 5 {
// skip two iterations
y = y + 2;
continue;
}
fmt.Printf("value is: %d\n", y);
y++;
}
}
TYPES
Basic Types
Data types specify the data types that can store in a valid Go variable. The
Go language divides types into various categories, which are as follows:
Numbers
Booleans
Strings
Numbers
In Go, numbers are divided into three subcategories, which are as follows:
Data
Type Description
int8 8-bit signed integer
int16 16-bit signed integer
int32 32-bit signed integer
int64 64-bit signed integer
uint8 8-bit unsigned integer
uint16 16-bit unsigned integer
uint32 32-bit unsigned integer
uint64 64-bit unsigned integer
Int In and uint have the same size, either 32 or 64 bits
Uint In and uint have the same size, either 32 or 64 bits
It is the same as int32 and represents Unicode code
Rune
points
Byte It is an abbreviation for uint8
It is an unsigned integer type. It does not have a defined
Uintptr
width, but it can hold all of the bits of a pointer value
Example:
Example:
// illustrate the use of floating-point numbers
package main
import "fmt"
func main()
{
c := 22.46
d := 35.88
// Subtract of two floating-point number
e := d-c
// Display the result
fmt.Printf("The Result is: %f", e)
// Display the type of c variable
fmt.Printf("\nThe type of e is : %T", e)
}
Booleans
The boolean data type only represents two values: true or false. Boolean
values are neither inherently nor explicitly changed to any other type.
Example:
Strings
A string data type comprises a sequence of Unicode code points. In other
words, a string is a collection of immutable bytes, which means that it
cannot change once it is produced. A string can contain any human-
readable data, including zero-value bytes.
Example:
// A program that demonstrates the use of strings.
package main
import "fmt"
func main()
{
// strng variable stores strings
strng := "PiiksofPiiks"
// Display length of the string
fmt.Printf("The Length of the string is:%d",
len(strng))
// Display the string
fmt.Printf("\nthe String is: %s", strng)
// Display the type of strng variable
fmt.Printf("\nType of strng is: %T", strng)
}
Type Conversion
T(v) is a function that converts the value v to the type T. Here are some
numerical conversions:
var c int = 43
var d float64 = float64(i)
var e uint = uint(f)
Type Assertion
We can use type assertion if you have a value and wish to convert it to
another or a specified type (in the case of an interface). A type assertion
takes a value and attempts to construct another version in the explicit type
supplied.
In the following example, the timeMap function accepts a value and, if it
can be represented as a map of interfaces{} keyed by strings, injects a new
entry named “updated at” with the current time value.
package main
import (
"fmt"
"time"
)
package main
import "fmt"
func main() {
k := &fakeString{"Ceci n'est pas un string"}
printString(k)
printString("Hello, Everyone")
if err != nil {
if msqlerr, ok := err.(*mysql.MySQLError); ok &&
msqlerr.Number == 1062 {
log.Println("We got MySQL duplicate:(")
} else {
return err
}
}
Structs
A struct is a group of fields/properties. New types can be defined as structs
or interfaces. If we come from an object-oriented background, think of a
struct as a lightweight class that allows for composition but not inheritance.
We do not need to declare getters and setters on struct fields because they
are automatically accessible. However, only exported (capitalized) fields
may be accessed outside a package.
By listing the values of its fields, a struct literal assigns a freshly
allocated struct value. Using the “Name:” syntax, we may list only a subset
of fields (the order of named fields is irrelevant when using this syntax).
The & prefix creates a reference to a newly allocated struct.
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println(Bootcamp{
Lat: 34.012837,
Lon: -118.495339,
Date: time.Now(),
})
}
package main
import "fmt"
var (
e = Point{1, 2} // has type Point
f = &Point{1, 2} // has type *Point
g = Point{X: 1} // D:0 is implicit
h = Point{} // X:0 and D:0
)
func main() {
fmt.Println(e, f, g, h)
}
package main
import (
"fmt"
"time"
)
func main() {
event := Bootcamp{
Lat: 34.012837,
Lon: -118.495339,
}
event.Date = time.Now()
fmt.Printf("Event on %s, location (%f, %f)",
event.Date, event.Lat, event.Lon)
Initializing
The new expression in Go allows us to allocate a zeroed variable of the
specified type and return a pointer to it.
c := new(int)
package main
import (
"fmt"
)
type Bootcamp struct {
Lat float64
Lon float64
}
func main() {
c := new(Bootcamp)
d := &Bootcamp{}
fmt.Println(*c == *d)
}
package main
import "fmt"
func main() {
p1 := Players{}
p1.Id = 28
p1.Name = "Katt"
p1.Location = "CA"
p1.GameId = 70414
fmt.Printf("%+v", p1)
}
import "fmt"
func main() {
p1 := Players{}
p1.Id = 28
p1.Name = "Katt"
p1.Location = "CA"
p1.GameId = 70414
fmt.Printf("%+v", p1)
}
package main
import "fmt"
func main() {
p1 := Players{
User{Id: 28, Name: "Katt", Location: "CA"},
70414,
}
fmt.Printf(
"Id: %d, Name: %s, Location: %s, Game id: %d\n",
p1.Id, p1.Name, p1.Location, p1.GameId)
// Directly set field defined on the Player struct
p1.Id = 16
fmt.Printf("%+v", p1)
}
We can’t merely pass the composed fields when using a struct literal with
an implicit composition. Instead, we must supply the types that make up the
struct. Once configured, the fields are immediately accessible.
Because our struct is made up of another struct, the User struct’s
functions are likewise accessible to the Player. Let us develop a way to
demonstrate that behavior:
package main
import "fmt"
type User struct {
Id int
Name, Location string
}
func main() {
p1 := Players{}
p1.Id = 28
p1.Name = "Katt"
p1.Location = "CA"
fmt.Println(p1.Greetings())
}
import (
"log"
"os"
)
func main() {
job := &Job{"demo", log.New(os.Stderr, "Job: ", log.Ldate)}
// same as job := &Job{Command: "demo",
// Logger: log.New(os.Stderr, "Job: ", log.Ldate)}
job.Logger.Print("test")
}
Our Job struct contains a Logger field, which is a reference to another type
(log.Logger)
We set the logger when we create our value so that we may call its Print
function later by chaining the calls: a job.Logger.Print()
However, Go allows us to go much farther by employing implicit
composition. We can forgo establishing the field for our logger because all
methods are now accessible via a reference to the log. Our struct has
loggers available:
1 https://www.GoLangbootcamp.com/book/types#sec-initializing_values
package main
import (
"log"
"os"
)
type Job struct {
Command string
*log.Logger
}
func main() {
job := &Job{"demo", log.New(os.Stderr, "Job: ", log.Ldate)}
job.Print("starting-now..")
}
We must still set the logger, which is frequently an excellent reason to use a
constructor (custom constructors are used when we need to set a structure
before utilizing a value). What’s excellent about implicit composition is that
it allows us to simply and inexpensively make our structs implement
interfaces. Assume we have a function that accepts variables and
implements an interface with the Print method by adding *log.Logger to
our struct (and properly initializing it), our struct is now implementing the
interface without the need for us to write any new methods.
mkdir go_modules
cd go_modules
Use the following command to set the current directory as the root of the
module, which will allow us to handle dependencies:
2 https://www.geeksforgeeks.org/how-to-create-modules-in-GoLang/
We can additionally create another Go file with the following code to test
the function as mentioned above:
package gfg_go
import (
"testing"
"fmt"
"strings"
)
// function to test if original
// go program is working
func TestFunction(test *testing.T){
test_string1 := "go_modules"
// calling function from
// previous go file
res := strings.Split(initialiser(), ":")
// removing spaces and line-ending
// punctuation before comparing
test_string2 := strings.Trim(res[1], ". ")
if test_string1 == test_string2 {
fmt.Printf("Successful!\n")
} else {
// this prints error message if
// strings don't match
test.Errorf("Error!\n")
}
}
We should observe that our test builds passed after executing the go test
command.
module go_modules
go 1.14
require github.com/rs/zerolog v1.14.3
Starting Out
Module Initializing
To begin, we must start the module within your project:
Inside, a go.mod file with the name of our project directory as the module
and the version of Go we are using will be produced.
Setting Up Dependencies
After initializing the go.mod file, we may use the go get command to get
specified versions of modules. Example:
$ go get -u github.com/gin-gonic/gin
The package manager will check for and download the dependency’s most
recent tag. Download all child dependencies required if the -u parameter is
used. The go.sum file will be added to our project after a dependency for
the project has been downloaded. The contents of the go.mod file are
changed to indicate all of the dependencies that have been downloaded in
their latest versions.
There will be times when we are creating code in our Go project, and the
dependencies have not been downloaded by the go get command but are
mentioned as an import statement. To confirm this, run/build our project,
which will automatically check for and download suitable versions of the
dependencies depending on our code.
$ go build
We may also just download the missing dependencies and not run/build our
project:
$ go mod download
$ go mod tidy -v
The program will produce the result shown below, and any unneeded
dependencies in the go.mod and go.sum files are deleted.
$ go mod tidy –v
Description Command
Install the latest tag
go get github.com/gin-gonic/gin
version
Install specific tag version go get github.com/gin-gonic/[email protected]
Install a specific branch go get github.com/gin-gonic/gin@master
go get github.com/gin-
Install specific commit
gonic/gin@1bdf86b
We may also alter the go.mod file and then run go mod download.
ERROR HANDLING
Errors suggest that something is wrong with the software. Assume we are
attempting to open a file that does not exist in the file system. This is an
aberrant condition, and an error indicates it.
In Go, errors are just values. The built-in error type is used to express
errors. Later in this course, we’ll learn more about the error type.
Error-values are kept in variables, supplied as parameters to functions,
returned from functions, and so on, much like any other built-in type, such
as int, float64, etc.
Let’s begin with an example program that attempts to open a file that
does not exist.
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Open("/test.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(f.Name(), "open-successfully")
}
We attempt to open the file /test.txt (which will not exist in the playground).
The os package’s Open function has the below given signature:
It only has one method with the signature Error() string. As an error, any
type that implements this interface can be used. This function returns an
error explanation.
When printing the error, the fmt.Println function internally uses the
Error() string method to obtain the error information.
We can analyze this error message to obtain the file path “/test.txt” of the
file that produced the issue. However, this is a crude method. In subsequent
versions of Go, the error description might change at any time, causing our
code to fail.
But do you think there is a more efficient way to obtain the file name?
Yes, it is possible, and the Go standard library employs various methods to
convey additional information about mistakes. Let’s take a look at them one
at a time.
PathError struct’s Path field contains the file’s path that generated
the error. Let’s change the code we built before to print the path.
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Open("test.txt")
if err != nil {
if pErr, ok := err.(*os.PathError); ok {
fmt.Println("Failed to open file at path",
pErr.Path)
return
}
fmt.Println("Generic-error", err)
return
}
fmt.Println(f.Name(), "open-successfully")
}
package main
import (
"fmt"
"net"
)
func main() {
addr, err := net.LookupHost("GoLangbot123.com")
if err != nil {
if dnsErr, ok := err.(*net.DNSError); ok {
if dnsErr.Timeout() {
fmt.Println("operation timed-out")
return
}
if dnsErr.Temporary() {
fmt.Println("temporary-error")
return
}
fmt.Println("Generic DNS-error", err)
return
}
fmt.Println("Generic-error", err)
return
}
fmt.Println(addr)
}
package main
import (
"fmt"
"path/filepath"
)
func main() {
files, err := filepath.Glob("[")
if err != nil {
if err == filepath.ErrBadPattern {
fmt.Println("Bad pattern-error:", err)
return
}
fmt.Println("Generic-error:", err)
return
}
fmt.Println("matched-files", files)
}
package main
import (
"fmt"
"path/filepath"
)
func main() {
files, _ := filepath.Glob("[")
fmt.Println("matched-files", files)
}
We already know that the pattern is incorrect from the prior case. In line 7,
we used the blank identifier to disregard the error provided by the Glob
function. In line 8, we just print the matched files. This program will print,
matched files []
Because we disregarded the error, the result shows that no files fit the
pattern, but the pattern itself is incorrect. As a result, never overlook
mistakes.
First Example:
Second Example:
Syntax:
Example:
FUNCTIONS IN Go
Functions are frequently coding blocks or statements in a program that
allow the user to reuse the same code, saving memory and time, and, most
importantly, increasing code readability. A function is, in essence, a
collection of statements that together perform a particular task and provide
the result to the caller. A function can also complete a job without returning
any results.
Function Declaration
A function declaration is a technique for creating a function.
Syntax:
func function_name(Parameter-list)(Return-type){
// function-body
}
Function Calling
When a client needs to run a function, they invoke or call it. The function’s
capabilities must be provided to be used. As shown in the example below,
we have an area () function with two parameters. We refer to this function
by its name, area(23, 12), with two parameters in the main function.
// Program to show the
// use of function
package main
import "fmt"
// area() is used to find the
// area of the rectangle
// area() function two parameters,
// i.e, length, width
func area(length, width int)int{
arr := length* width
return arr
}
// main-function
func main() {
// Display area of the rectangle with
// method calling
fmt.Printf("Area of rectangle is : %d", area(23,
12))
}
Function Arguments
In Go, the arguments sent to a function are known as real parameters, but
the parameters received by a function are known as formal parameters.
The Go language utilizes the call by value mechanism by default to pass
parameters in a function. For providing parameters to our function, the Go
programming language gives two options:
Example:
// Program to demonstrate the
// concept of call by value
package main
import "fmt"
// the function which swap values
func swap(c, d int)int{
var o int
o= c
c=d
d=o
return o
}
// main-function
func main() {
var x int = 27
var y int = 40
fmt.Printf("x = %d and y = %d", x, y)
// call by values
swap(x, y)
fmt.Printf("\n x = %d and y = %d",x, y)
}
Call by reference: Because the actual and formal parameters both
point to the same place, any changes made within the function are
mirrored in the caller’s parameter list.
Example:
Syntax:
func function_name(parameter_list)(returntype_list){
// code
}
Example:
// program to show how a function may return several
values
package main
import "fmt"
// myfunc return three values of the int type
func myfunc(c, d int)(int, int, int ){
return c - d, c * d, c + d
}
// main-Method
func main() {
// return values are assigned into the different
variables
var myvar1, myvar2, myvar3 = myfunc(41, 22)
// Displayvalues
fmt.Printf("Result is: %d", myvar1 )
fmt.Printf("\nResult is: %d", myvar2)
fmt.Printf("\nResult is: %d", myvar3)
}
Syntax:
func function_name(para1, para2 int)(name1 int,
name2 int){
// code
}
Example:
// show how to give names to return the values
package main
import "fmt"
// myfunc return two values of int type
// here return the value name is rectangle & square
func myfunc(c, d int)( rectangle int, square int ){
rectangle = c*d
square = c*c
return
}
func main() {
// return values are assigned into the two
different variables
var area1, area2 = myfunc(14, 18)
// Display-values
fmt.Printf("The Area of rectangle is: %d", area1
)
fmt.Printf("\nThe Area of square is: %d", area2)
}
VARIADIC FUNCTIONS
A variadic function takes a variable set of variables to invoke. In other
words, the variadic function will accept zero or more arguments from the
user.fmt. Printf is an example of a variadic function; it takes one fixed
parameter at the beginning and can take any number of arguments
afterward.
Syntax:
Example:
ANONYMOUS FUNCTION
The Go scripting language has an anonymous function. An anonymous
function lacks a name. When it is necessary to write an inline function, an
anonymous function can create a closure in Go. Anonymous functions are
also known as function literals.
Syntax:
func(parameter_list)(return_type){
// code
// Use return-statement if returntype are given
// if return_type is not given, then do not
// use the return statement
return
}()
Example:
main() Function
In Go, the main package is a one-of-a-kind package that contains
executable programs, such as the main() function. The main() function is a
one-of-a-kind function that acts as the executable program’s starting point.
It takes no parameters and returns none. There is no need to invoke the
main() method directly because Go executes it for you, and every
executable program must include a single main package and the main()
function.
Example:
// Program to illustrate the concept of main()
function
// Declaration of the main package
package main
// Importing-packages
import (
"fmt"
"sort"
"strings"
"time"
)
// Main-function
func main() {
// Sorting given slice
str := []int{455, 39, 223, 29, 86, 38, 427, 29}
sort.Ints(str)
fmt.Println("Sorted slice: ", str)
// Finding-index
fmt.Println("The Index value is: ",
strings.Index("Hello", "ks"))
// Finding-time
fmt.Println("Time is: ", time.Now().Unix())
}
init() Function
Like the main function, the init() function accepts no parameters and returns
nothing. This function is included in every package and is called when the
package is loaded for the first time. Because this function is declared
implicitly, we can’t use it anywhere else. In the same application, we can
build many init() functions and will execute them in the order in which they
are built. Init() functions can be inserted anywhere in the program and are
called in the order of the lexical file name. And statements are allowed if
the init() function is used, but please remember that the init() method is
executed before the main() function call, so it is not dependent on the
main() function.
Example:
// Program to show the
// tconcept of init() function
// Declaration of the main package
package main
// importing-package
import "fmt"
// multiple init()-function
func init() {
fmt.Println("Welcome home")
}
func init() {
fmt.Println("Hello everybody ")
}
// main-function
func main() {
fmt.Println("Welcome to class")
}
DOI: 10.1201/9781003309055-6
IN THIS CHAPTER
➢ Optimization tips
➢ Writing secure code
➢ Best coding practices
➢ Security and hardening ideas
import "github.com/gorilla/mux"
import "net/http/pprof"
var handler *mux.Router
// .....
handler.PathPrefix("/debug/pprof/profile").HandlerFunc(p
prof.Profile)
handler.PathPrefix("/debug/pprof/heap").HandlerFunc(ppr
of.Heap)
Result:
12910ms of 24020ms total (56.79%)
Dropped 481 nodes (cum <= 120.10ms)
Showing top 30 nodes out of 275 (cum >= 160ms)
flat flat% sum% cum cum%
1110ms 4.62% 4.62% 2360ms 9.83%
runtime.mallocgc
940ms 3.91% 8.53% 1450ms 6.04%
runtime.scanobject
830ms 3.46% 11.99% 830ms 3.46% runtime.futex
800ms 3.33% 15.32% 800ms 3.33%
runtime.mSpan_Sweep.func1
750ms 3.12% 18.44% 750ms 3.12%
runtime.cmpbody
720ms 3.00% 21.44% 720ms 3.00% runtime.xchg
580ms 2.41% 23.86% 580ms 2.41%
runtime._ExternalCode
Result:
What this does is output the source lines of the function we’re
interested in and count which statements generated heap allocations
within the function. This is one of several commands available within
pprof. Another handy one is peeking, which displays the callers and
callees of functions. By entering “help” and playing with what we see,
we can receive a comprehensive list.1
1https://www.splunk.com/en_us/blog/devops/a-pattern-for-optimizing-
go-2.html
Result:
Total: 11323262665
ROUTINE ========================
github.com/signalfuse/sfxgo/ingest/bus/rawbus.
(*Partitioner).Partition in
/opt/jenkins/workspace/ingest/gopath/src/github.com/signa
lfuse/sfxgo/ingest/bus/rawbus/partitioner.go
927405893 927405893 (flat, cum) 8.19% of Total
. . 64: if ringSize == 0 {
. . 65: return 0, ErrUnsetRingSize
. . 66: }
. . 67: var b1 [8]byte
. . 68: binary.LittleEndian.PutUint64(b1[:],
uint64(message.Key.(*partitionPickingKey).tsid))
239971917 239971917 69: logherd.Debug2(log,
"key", message.Key, "numP", numPartitions,
"Partitioning")
. . 70: murmHash := murmur3.Sum32(b1[:])
. . 71:
. . 72: // 34026 => 66
. . 73: setBits := uint(16)
. . 74: setSize := uint32(1 << setBits)
. . 75: shortHash := murmHash & (setSize -
1)
. . 76: smallIndex := int32(shortHash) *
int32(k.ringSize) / int32(setSize)
687433976 687433976 77: logherd.Debug3(log,
"smallIndex", smallIndex, "murmHash", murmHash,
"shortHash", shortHash, "Sending to partition")
. . 78: return smallIndex, nil
. . 79:}
. . 80:
Result:
What this implies is that the essential sections are on line 77, which
shows that smallIndex, murmHash, and shortHash all escape to the
heap. I’m producing more objects on the heap than we need since the
compiler executes short-lived heap allocations.
Step 6: Benchmark the Partition Function
What we should write:
This runs benchmarks that match the regex “.” and -benchmem will
also run benchmarks that measure heap use on average each iteration.
By supplying _NONE_ to -run, we’re instructing tests to execute unit
tests with the “_NONE_” string inside them, which saves us some
time. In other words, run all of the benchmarks but none of the unit
tests.
Result:
PASS
BenchmarkPartition-8 10000000 202 ns/op 64 B/op
4 allocs/op
Result:
Result:
PASS
BenchmarkPartition-8 30000000 40.5 ns/op 0
B/op 0 allocs/op
ok github.com/signalfuse/sfxgo/ingest/bus/rawbus 1.267s
This implies that each operation now takes only 40ns, and there are no
allocations per operation. This is the essential element for us because we
attempted to optimize heap consumption.
Improved Consistency
Consistency is like cleaning; no one sees it when it’s done well, but when
neglected, the entire home looks sloppy, and we find ourselves in disarray.
Accomplishing perfect consistency is difficult since guaranteeing
backward compatibility can hinder progress, but paying attention to
employing clear coding principles, compatible APIs, and consistent
standards can undoubtedly alleviate the suffering.
Maintaining code consistency in mind is especially crucial when dealing
with legacy code or more significant projects with multiple developers.
Enhanced Workflow
Many web development projects are managed by remote or distributed
teams, such as open-source communities. One of the most challenging
aspects of managing such a process is making communication effective
enough for team members to understand each other readily and not explain
settings continuously.
Best practices and style guidelines that are agreed upon may bridge the
gap between people from diverse backgrounds, not to mention the regular
communication challenges that most online projects have between design
and development teams.
Code optimization is also workflow optimization since if team members
speak the same language and have the same stated goals, they will be able
to work together much more quickly.
Efficiency of Algorithms
A program can do the same task in various methods with variable
magnitudes. By using algorithm optimization, we will get maximum
performance in our application. Continue reading our article to meet and
overcome the three efficiencies mentioned above (latency, resource, and
algorithm) in our GoLang application. It is a hand-picked list compiled with
the assistance of our knowledgeable Go engineers.
When we hire a GoLang developer from us, we will receive all of the
experience required to create an application for our users.
package main
import (
"fmt"
"gopkg.in/go-playground/validator.v9"
)
func main() {
v1 := validator.New()
a1 := User{
Email: "a1",
}
err := v1.Struct(a1)
2 https://blog.sqreen.com/top-6-security-best-practices-for-go/
package main
import (
"html/template"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
param1 := r.URL.Query().Get("param1")
tmpl := template.New("heyyy")
tmpl, _ = tmpl.Parse('{{define "T"}}{{.}}{{end}}')
tmpl.ExecuteTemplate(w, "T", param1)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
But what if the database engine doesn’t support prepared statements? What
if it has an impact on query performance? We can use the db.Query()
method, but we must first sanitize the user’s input, as shown in earlier
sections. Other third-party libraries are available to prevent SQL injections,
such as sqlmap.
Despite our best efforts, vulnerabilities occasionally sneak through or
reach our apps via third parties. Consider using an application security
management platform like Sqreen to defend our web apps against essential
threats like SQL injections.
package main
import (
"database/sql"
"context"
"fmt"
"GoLang.org/x/crypto/bcrypt"
)
func main() {
ctx := context.Background()
email := []byte("[email protected]")
password := []byte("47;u5:B(95m72;Xq")
hashedPassword, err :=
bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
if err != nil {
panic(err)
}
package main
import (
"crypto/tls"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req
*http.Request) {
w1.Header().Add("Strict-Transport-Security", "max-
age=53072000; includeSubDomains")
w1.Write([]byte("This is server example.\n"))
})
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521,
tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
}
srv := &http.Server{
Addr: ":443",
Handler: mux,
TLSConfig: cfg,
TLSNextProto: make(map[string]func(*http.Server,
*tls.Conn, http.Handler), 0),
}
log.Fatal(srv.ListenAndServeTLS("tls.crt", "tls.key"))
}
It’s worth noting that the app will be listening on port 443. The line that
enforces the HTTPS setting is as follows:
w.Header().Add("Strict-Transport-Security", "max-
age=53072000; includeSubDomains")
We may also wish to include the server name in the TLS settings, as seen
below:
if err != nil {
// handle error
}
In addition, Go provides a native library for working with logs. The most
straightforward basic code is as follows:
package main
import (
"log"
)
func main() {
log.Print("Logging in the Go")
}
However, there are third-party logging libraries available. Logrus, glog, and
loggo are a few examples. Here’s a little bit of logrus code:
package main
import (
"os"
log "github.com/sirupsen/logrus"
)
func main() {
file, err := os.OpenFile("info.log",
os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
log.SetOutput(file)
log.SetFormatter(&log.JSONFormatter{})
log.SetLevel(log.WarnLevel)
log.WithFields(log.Fields{
"animal": "fish",
"size": 20,
}).Info("A group of fish emerges from the ocean")
}
Finally, ensure that we follow all preceding guidelines, such as encryption
and sanitization of the data we enter into the logs.
Syntax
Go is a compiled static language. This means that there are some ground
rules that we must follow. This will help reduce confusion while working in
a large team when each group must build its code and verify it is compatible
with the others.
func main(){
var c int = 2022
//do-something
}
In Go, use MixedCase for all names. Do not utilize underscores in the
same way that Python does.
Acronyms should be written in all capital letters.
Keep local variables to a minimum. Common variables, such as an
index or a loop parameter, can be represented by a single letter.
import (
"fmt"
"io/ioutil"
"net/http"
"os"
// Public Go Module for the logging
log "github.com/sirupsen/logrus"
)
DOI: 10.1201/9781003309055-7
IN THIS CHAPTER
go get github.com/boombuler/barcode
Development
Main.go Source Code
The main function starts with a call to http.HandleFunc instructs the http
package to use homeHandler to handle all requests to the web root (“/”).
homeHandler is a function of the type http.HandlerFunc. Its parameters are
an http.ResponseWriter and an http.Request.
ViewCodeHandler is a function that allows visitors to examine a created
QR-code on a new page. It will handle URLs that begin with “/generator/,”
the template for the function. The contents of generator.html will be read by
ParseFiles and returned as a *template.
package main
import (
"image/png"
"net/http"
"text/template"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/qr"
)
func main() {
http.HandleFunc("/", homeHandler)
http.HandleFunc("/generator/", viewCodeHandler)
http.ListenAndServe(":8080", nil)
}
png.Encode(w, qrCode)
}
The FormValue function returns the value of the dataString input field,
which is used to produce the QR code with the Encode method.
<h1>{{.Title}}</h1>
<div>Enter string we want to QRCode.</div>
<form action="generator/" method=post>
<input type="text" name="dataString">
<input type="submit" value="Submit">
</form>
Implementation
Using the command line or putty, execute the run command “Go ahead and
run main.go”.
// go/src/http-client/main.go
package main
import (
"fmt"
"io/ioutil"
"net/http"
"time"
)
func main() {
c1 := http.Client{Timeout: time.Duration(2) * time.Second}
resp, err := c1.Get("https://go.dev/")
if err != nil {
fmt.Printf("Error %s", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error %s", err)
return
}
....
resp, err := c1.Get("https://go.dev/")
if err != nil {
fmt.Printf("Error %s", err)
return
}
....
The code snippet above is identical to making a POST request to a URL.
However, in this scenario, we must add the data we are providing alongside
the POST request to the body of the request to the webserver.
....
postData := bytes.NewBuffer([]byte('{"post":"boom-boom
library"}'))
resp, err := c1.Post("https://go.dev/", "application/json",
postData)
if err != nil {
fmt.Printf("Error %s", err)
return
}
....
....
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error %s", err)
return
}
fmt.Printf("Body : %s", body)
....
// go/src/http-client/main.go
package main
import (
"fmt"
"io/ioutil"
"net/http"
"time"
)
func main() {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error %s", err)
return
}
In the preceding code, we built a client and then specified a new request
using the http.NewRequest function. We described the type of request we
wanted using the parameters.
The function signature for http.Request is as follows:
The first argument specifies the request’s method. Then, in the second
argument, we supply the URL and the body to store the data or nil in the
case of a GET request because there is nobody to transmit.
Then, as seen below, we define the Header we wish to attach to the
request:
req.Header.Add("Accepted", 'application/json')
Header fields are used to add and convey additional information about the
request to the server. The HTTP 1/1 protocol includes numerous Header
fields:
....
req, err = http.NewRequest("GET", "https://www.xxxx.xxx",
nil)
req.Header.Add("Accept", 'application/json')
req.Header.Add("Authorization", fmt.Sprintf("token %s",
os.Getenv("TOKEN"))
....
func main() {
// starting the API
handleReq()
}
Let’s expand on this by adding another endpoint. We’ll modify the code
above to include a new function, return_contact().
package main
// important-libraries
import (
"fmt"
"log"
"net/http"
)
// Home-function
func Home()(w http.ResponseWriter, r *http.Request){
// This is what the function will print.
fmt.Fprintf(w, "Welcome to Educative Home!")
}
func return_contact()(w http.ResponseWriter, r *http.Request){
// This is what the function will print.
fmt.Fprintf(w, "Email: [email protected]")
}
// function to handle the requests
func handleReq() {
// will call the Home function by default.
http.HandleFunc("/", Home)
http.HandleFunc("/contact", return_contact)
log.Fatal(http.ListenAndServe(":8200", nil))
}
func main() {
// starting API
handleReq()
}
A user may now reach the endpoint contact by typing and inputting the
URL:
http://localhost:8200/contact
Project Setup
mkdir GoLang-api
cd GoLang-api
go mod init github.com/cameronldroberts/GoLang-api
touch main.go
We’re finally ready to get started with the coding. Open the GoLang-api
project in our preferred editor and navigate to main.go, which contains the
code.
Imports
First, we’ll go over imports, which are how we bring in other Go modules.
A module in Go is a grouping of one or more packages that contain related
code. The following four will be used to access the API. Depending on the
editor (and settings) we use, imports may be handled automatically, and we
may not need to perform the next step of manually adding imports.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
Example of a Response
This is an example of a response from the API when we call it. We can
transfer the structure of the JSON answer that we will get into a struct
because we know its structure.
{
"id": "XgVnOK6USnb",
"jokes": "What did the calculator say to the student? We can
count on us",
"status": 200
}
Struct
Because the struct is quite simple, mapping from JSON to the struct below
would not be too complex in this case.
Code
Finally, a method invokes the API and processes the response. It is not
recommended to place function logic within main() while working with Go
because this is the entry point for our application. For the sake of this essay,
we will put the code in main, however this is not recommended while
programming in the real world.
func main() {
fmt.Println("Calling API....")
client := &http.Client{}
req, err := http.NewRequest("GET", "
http://www.laughfactory.com/jokes/clean-jokes", nil)
if err != nil {
fmt.Print(err.Error())
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
fmt.Print(err.Error())
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Print(err.Error())
}
var responseObject Response
json.Unmarshal(bodyBytes, &responseObject)
fmt.Printf("API Response as a struct %+v\n",
responseObject)
}
go run main.go
The main will be compiled and run as a result of this. Go ahead and file. If
everything went correctly, we should have jokes written into our terminal.
The output should look like this:
go run main.go
Calling API....
API Response as struct {ID:5h399pWLmyd Jokes:What did
the beaver say to the tree? It's been nice gnawing us.
Status:200}
Complete code:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type Response struct {
ID string 'json:"id"'
Jokes string 'json:"jokes"'
Status int 'json:"status"'
}
func main() {
fmt.Println("Calling API....")
client := &http.Client{}
req, err := http.NewRequest("GET", "
http://www.laughfactory.com/jokes/clean-jokes", nil)
if err != nil {
fmt.Print(err.Error())
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
fmt.Print(err.Error())
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Print(err.Error())
}
var responseObject Response
json.Unmarshal(bodyBytes, &responseObject)
fmt.Printf("API Response as a struct %+v\n", responseObject)
}
DOI: 10.1201/9781003309055-8
Go IS FAST
Go is a speedy scripting language. It is compiled to machine code and will
automatically outperform interpreted or virtual runtime languages. Go apps
compile rapidly as well, and the final binary is small. Our API delivers an
11.5 MB executable file in seconds.
EASY TO UNDERSTAND
Go’s syntax is short compared to other languages, making it easy to learn.
We can recall most of it, so we don’t need to spend much time looking it up.
It’s also immaculate and easy to read. Non-Go programmers, especially
those accustomed to C-style syntax, can usually read a Go program and
comprehend what’s going on.
STATIC TYPING
Go is a computer language that is strongly typed and statically typed.
Integer, byte, and string are examples of primitive types. Another sort of
structure is structure. Like any strongly typed language, the type system
allows the compiler to catch entire classes of issues. Go has simple-to-use
built-in types for lists and maps.
TYPES OF INTERFACES
In Go, interfaces exist, and any struct can implement an interface by simply
implementing its methods. This helps you to decouple the dependencies in
our code. Dependencies can then mock in tests. Using interfaces, we may
create more modular, testable code. Go also has first-class functions, which
helps us write more functional code.
STANDARD LIBRARY
Go includes an excellent standard library. It has handy built-in functions for
working with primitive types. Packages make it easy to set up a web server,
manage I/O, interface with encryption, and process raw data. JSON
serialization and deserialization in the standard library are straightforward.
Using “tags,” we can add JSON field names directly next to struct fields.
GARBAGE COLLECTION
Memory management in Go was more straightforward than that in C and
C++. Objects that have been dynamically allocated are destroyed. Because
it does not support pointer arithmetic, Go makes using pointers safer. It also
supports the use of value types.
TESTING ASSISTANCE
Testing support is included in the standard library. There is no need for
reliance. If we have a code called example.go ahead and save our tests in a
file named thing test.go, and then run “go test” Go will perform these tests
rapidly.
GoLang’s Definition
Google’s GoLang programming language is an open-source project. It was
designed to be a better choice for creating big programs, particularly those
that require concurrency capabilities, such as channels and goroutines.
GoLang can also compile code into the machine’s native instruction set,
allowing it to execute quicker than other languages.
GoLang’s FUTURE
GoLang’s future appears promising. The language will continue to be
developed as developer demands evolve, and it will very certainly become
more popular in the future years. As a result, if we don’t want to fall behind,
we should plan on learning it, as GoLang is superior to other programming
languages.
IS Go AN OBJECT-ORIENTED PROGRAMMING
LANGUAGE?
Both yes and no. There is no type hierarchy in Go, despite the fact that it
contains types and functions and supports object-oriented programming.
The idea of “interface” in Go offers a distinct approach that we feel is easier
to use and more generic in certain aspects. There are various techniques to
embed types in other types to give a similar – but not identical – to
subclassing experience. Furthermore, Go methods are more broad than C++
or Java methods in that they may be written for any form of data, including
built-in types, such as raw, “unboxed” integers. They are not limited to
structs (classes).
Furthermore, the lack of a type hierarchy makes “things” in Go feel
considerably lighter than in languages like C++ or Java.
Go took principles from procedural programming, functional
programming, and object-oriented programming and combined them, while
leaving out others, to develop its own distinct flavor of idiomatic
programming style.
Encapsulation
One of the nicest aspects of Go is that capitalized fields, methods, and
functions are all public. All other fields are unique to the package and are
not exported. We can tell if anything is public or private with a single
glance. There are no safeguards since there is no legacy.
Interfaces
Forget about Java and PHP-style interfaces. Interfaces in Go are
considerably different, and one important aspect is that interfaces are met
implicitly.
Interfaces are often relatively tiny, consisting of only one method. In
idiomatic Go, we won’t find extensive lists of methods.
Interfaces neatly provide polymorphism: by adopting an interface, we
announce our readiness to embrace any kind of object that satisfies that
interface.
Methods
Methods exist for types. They are specified outside of the type definition,
using a syntax that may be familiar to JavaScript prototype method
declarations.
Less Bloat
Overall, the Go object-oriented programming approach is highly versatile
and clear. Leaving classes and inheritance behind, we’ll notice very little
boilerplate, and instead of arguing over the ideal hierarchical structure for
classes, which becomes difficult to modify, we’ll have the ability to
compose and decompose types as needed.
Distributed Services
Concurrency is required for services dispersed across a network, such as
those found in a microservices architecture. Because of Go’s advantages in
this area are well-suited for developing solutions that adhere to a distributed
services model.
Using goroutines, software developers may provide the scalability
required to manage several threads simultaneously. In addition to Go
channels, this functionality creates the concurrency required for distributed
network services. These Go code artifacts offer the communication object
that allows goroutines to communicate information. In addition to
maintaining the contact between goroutines, these objects offer the
synchronization required for scalable event processing across dispersed
network systems.
Cloud-Native Development
Because of its concurrency and networking features, Go has emerged as the
language of cloud infrastructure. When we consider that Google built Go
specifically for this reason, its features correspond nicely with the needs of
cloud-native apps. Go’s creators designed it for portability in addition to its
built-in concurrency and networking features. These three characteristics
make it a viable solution for developing cloud-based apps.
The mobility of GoLang is due to its cross-platform compatibility. We
just need to write the code once using Go, and we can then deploy it
wherever. Because we only need to handle one codebase, this feature makes
maintainability significantly more realistic. If the software has to be
changed, we simply need to make one modification and then build it for the
appropriate deployment platform, whether Windows, Linux, or macOS. It
also supports x86, ARM, and other specialized technologies like ppc64 and
MIPS, so we may use it on services that use diverse CPU architectures.
Machine Learning
Go’s adaptability as a general-purpose, cross-platform programming
language allows software developers to utilize its machine learning
capabilities. As a statically typed programming language, its binaries offer
the speed and performance required for CPU-intensive, high computation
computations. Its scalability, parallelism, and portability also provide data
scientists with the flexibility to construct large-scale machine learning
systems.
There are various machine learning libraries created in Go, in addition to
its built-in functionality. Platforms, such as GoLearn, Gorgonia, and goml,
are just a few examples. Using these technologies, software developers may
integrate their code without relying on other libraries developed in a
different programming language. Furthermore, because solution architects
only need to handle a single Go codebase, this integration provides
excellent portability and maintainability.
DOI: 10.1201/9781003309055-9
HEYYY EVERYONE
heyyy.go
package main
import "fmt"
func main() {
message := greetMe("everyone")
fmt.Println(message)
}
func greetMe(name string) string {
return "Heyyy, " + name + "!"
}
Run:
$ go build
VARIABLES
Variable declaration:
CONSTANTS
const Phi = 1.818
Basic Types
Strings
str := "Heyyy"
str := 'Multiline
string'
Numbers
Typical types:
// var-numbers [5]int
numbers := [...]int{0, 0, 0, 0, 0}
Slices
Pointers
func main () {
x := *getPointer()
fmt.Println("Value is", x)
}
func getPointer () (myPointer *int) {
y := 134
return &a
}
y:= new(int)
*y = 234
Type Conversions
x := 2
y := float64(x)
z := uint(x)
FLOW CONTROL
Conditional
if day == "saturday" || day == "sunday" {
rest()
} else if day == "tuesday" && isTired() {
groan()
} else {
work()
}
Statements in if
if _, err := doThing(); err != nil {
fmt.Println("Uh-oh")
}
Switch
switch day {
case "saturday":
// cases do not "fall through" by default!
fallthrough
case "sunday":
rest()
default:
work()
}
For Loop
for count := 0; count <= 20; count++ {
fmt.Println("Counter is at", count)
}
For-Range Loop
entry := []string{"Jacky","Rohan","Lonas"}
for x, val := range entry {
fmt.Printf("At position %d, character %s is
present\n", x, val)
}
While Loop
k := 0
a := 42
for k != a {
k := guess()
}
FUNCTIONS
Lambdas
myfunc := func() bool {
return a > 20000
}
PACKAGES
Importing
import "fmt"
import "math/rand"
import (
"fmt" // give fmt.Println
"math/rand" // give rand.Intn
)
Aliases
import x "math/rand"
x.Intn()
Exporting Names
func Heyyy () {
···
}
Packages
package heyyy
CONCURRENCY
Goroutines
func main() {
// A "channel"
ch := make(chan string)
// Start the concurrent routines
go push("Loe", ch)
go push("Harry", ch)
go push("Kurly", ch)
// Read the 3 results
// (Since our goroutines are concurrent,
// the order isn't guaranteed)
fmt.Println(<-ch, <-ch, <-ch)
}
func push(name string, ch chan string) {
msg := "Hello, " + name
ch <- msg
}
Buffered Channels
ch := make(chan int, 2)
ch <- 1
ch <- 2
ch <- 3
// fatal error:
// all goroutines are asleep - deadlock!
Closing Channels
Closes a channel
ch <- 1
ch <- 2
ch <- 3
close(ch)
for x := range ch {
···
}
Closed if ok == false
k, ok := <- ch
WaitGroup
import "sync"
func main() {
var wg sync.WaitGroup
for _, item := range itemList {
// Increment the WaitGroup Counter
wg.Add(1)
go doOperation(&wg, item)
}
// Wait for the goroutines to finish
wg.Wait()
}
func doOperation(wg *sync.WaitGroup, item string) {
defer wg.Done()
// do operation on the item
// ....
}
ERROR CONTROL
Defer
func main() {
defer fmt.Println("Done")
fmt.Println("Working")
}
Deferring Functions
func main() {
defer func() {
fmt.Println("Done")
}()
fmt.Println("Working")
}
Unless we provide a reference to acquire the final value at the end of main,
the defer func utilizes the current value of x.
STRUCTS
Defining
type Vertex struct {
A int
B int
}
func main() {
v := Vertex{1, 2}
v.A = 4
fmt.Println(v.A, v.B)
}
Literals
v := Vertex{A: 1, B: 2}
// Field names can omitt
v := Vertex{1, 2}
// B is implicit
v := Vertex{A: 1}
Pointers to Structs
v := &Vertex{1, 2}
v.A = 2
METHODS
Receivers
type Vertex struct {
A, B float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.A * v.A + v.B * v.B)
}
v := Vertex{1, 2}
v.Abs()
Mutation
func (v *Vertex) Scale(f float64) {
v.A = v.A * f
v.B = v.B * f
}
v := Vertex{6, 13}
v.Scale(0.5)
// 'v' is updated
Interfaces
Basic interface:
type Shape interface {
Area() float64
Perimeter() float64
}
Struct
type Rectangle struct {
Length, Width float64
}
Methods
func (k Rectangle) Area() float64 {
return k.Length * k.Width
}
func (k Rectangle) Perimeter() float64 {
return 2 * (k.Length + k.Width)
}
Interface Example
func main() {
var k Shape = Rectangle{Length: 3, Width: 4}
fmt.Printf("Type of k: %T, Area: %v, Perimeter: %v.",
k, k.Area(), k.Perimeter())
}
Bibliography
B
Backticks(“),using, 83–84
BDD, see Behavior Driven Development
Behavior Driven Development (BDD), 181
Benefits of using GoLang, 307
Bitwise operators, 53–54
Blank Identifier, 76, 96
Boolean constant, 42–43
Boolean data type, 30, 224–225
Break statement, 220, 285
Buffered channels, 324
Buffered I/O, making use of, 273
bufio.NewReader(os.Stdin), 21
Built-in testing and profiling framework, 7
Business, GoLang for, 311
C
Capability of GoLang, 316
cloud-native development, 317–318
creating solutions using Go, 316
distributed services, 317
machine learning, 318
server, development on, 317
versatility of GoLang, 318
web, applications for, 316–317
Cgo program, 17
reducing/eliminating the use of, 272
Closing channels, 324
Closures, 100
Cloud-native development, 317–318
Code optimization, 259
CI/CD workflows, 286
ensuring immutability and availability using GOPROXY,
287
making use of Go modules, 286–287
necessity of, 267
code base that is cleaner, 268
code upkeep, 270
easier to understand debugging, 269
enhanced workflow, 269–270
improved code readability, 269
improved consistency, 268
improved feature development, 270
more efficient refactoring, 269
pages that load faster, 268
performance, of GoLang, 270
asynchronous I/O operations, 272
binary, using, 273
buffered I/O, making use of, 273
CPU work, 272
efficiency of algorithms, 271
Goroutines sparingly, using, 271
hot spots, memory not allocating in, 272
int keys, 273
latency, optimization of, 271
lock-free algorithms, making use of, 272
patterns of GoLang application performance, 271
Protocol Buffers and MessagePack, 273
read-only locks, preferring, 272
reducing/eliminating the use of “cgo”, 272
regular expressions, making use of, 273
resource efficiency, optimization of, 270–271
slices, 273
StringBuffer/StringBuilder, 273
timeouts, 272
performance optimization, GoLang, 270
secure coding, GoLang for, 273
being cautious of mistakes and logs, 282–283
Contrast Go agent, 276
HTML templates, making use of, 278
input entries, verifying, 277
instrument-based security work, 275–276
making HTTPS communication mandatory, 280–282
need for integrated, automated security, 274–275
sensitive data, encrypting, 279–280
SQL injections, protecting ourselves against, 278–279
stuck in a rut with manual security, 273–274
syntax, 284
assigning data types to newly created variables, 284
avoid nesting by dealing with mistakes first, 285
declaring a variable/importing a package that we do not require,
284
Go code organization, 286
knowledge transfer, making use of comments to help in, 285
proper naming conventions, following, 284
simple, readable, and maintainable code, 285
type switch, using, 285–286
utility repeats, avoiding, 285
Color package, 174–175
Comments, 25–26
Common Vulnerabilities and Exposures (CVEs), 275
Communicating Sequential Processes (CSP), 194
Companies that use GoLang Google Cloud Platform, 308
Compare() method, using, 249–250
Complex constant, 40–41
Complex numbers, 29–30, 224
Composition vs. inheritance, 230–235
Concurrency, 193, 333–335
communicate to share memory, 204–206
Goroutines, 193–194
201
creating, 202
Go concurrent programming, 201
multithreading, issues with, 201–202
Goroutines to access shared resources, 207
access shared resources using Goroutines, 207
atomic counters, using, 209–210
benefits of concurrency, 211
concurrency disadvantages, 211
concurrency issues, 210–211
mutual exclusion, using, 207–209
problems with concurrency, 211–212
multiple Goroutines, 202
Goroutines’ advantages over threads, 204
parallelism and concurrency, difference between, 204
multithreading and, 6
producer-consumer problem, example of, 198
sleeping barber issue, illustration of, 195–198
Concurrency tools, 309
Concurrent programming, 304
Constants, 38, 320–321
boolean constant, 42–43
declaration, 39
numeric constant, 39
complex constant, 40–41
floating type constant, 41
integer constant, 40
string literals, 41–42
Continue statement, 221
Contrast Go program, 275–276
Control statements, 57
if..else..if ladder, 61–63
if statement, 57–58
if…else statement, 58–60
nested if statement, 60–61
Core capability of GoLang, 5–6
C program and Go, 17
CPU cores, 306
Create() function, 17
CSP, see Communicating Sequential Processes
CVEs, see Common Vulnerabilities and Exposures
D
Fatalf, 21–22, 24
Fetching packages, 191
First Go program, writing, 10
Go language, 11
hardware restrictions, 12
other programming languages vs. Go, 11–12
upsides and downsides of Go language, 12–13
Flexible programming language, Go as, 305–306
Floating-point numbers, 29, 223–234
Floating type constant, 41
Flow control, 213, 321–322
loop control statements in Go language, 219
break statement, 220
continue statement, 221
Goto statement, 220–221
fmt module, 20
For loop, 322
as basic as possible, 214–215
for channel, 219
as infinite loop, 215–216
in the map, 218–219
simple range in, 217
for strings, 218
using, 75
using a Blank Identifier in, 76
using range in, 76
while for loop, 216–217
For-range loop, 322
Function arguments, 252–253
Function calling, 251–252
Function declaration, 250–251
Functions, 322–333
handling errors in, 143–144
Future of GoLang, 306, 310
H
handleReq() method, 297
Hardware, Go empowering, 6–7
Home() function, 297
homeHandler, 290
HTML templates, making use of, 278
HTTP Authorization request header, 296–297
HTTP client, 291–293
POST and GET requests, 293–294
request headers, adding, 294–296
Identifiers, 26
If..else..if ladder, 61–63
If statement, 57–58, 321
If…else statement, 58–60
Import declaration, 167–168
Importing packages, 165
Import path, 167
Inheritance, 121–123
composition vs., 230–235
Init() function, 166–167, 257–258
Input entries, verifying, 277
inputReader.ReadString(‘n’), 21
Installation of Go
on Mac, 14
first program, making, 16
Go programs’ interaction with programs written in C/C++, 17
implementing Go program, 16–17
on Windows, 8
determining preinstalled version, 9
downloading and installing Go, 9–10
Instrument-based security work, 275–276
Integer constant, 40
Integers, 28–29, 222–223
Interfaces, 118, 327
implementing, 119–120
making, 119
polymorphism using, 123–124
redundant functions, 120–121
types of, 304
Interpretation, 311
Intrinsic capabilities of GoLang, 316
Ints, 78
IntsAreSorted, 78–79
ioutil.ReadFile(), 20
ioutil.WriteFile(), 21
IsNotExist() function, 18
J
Keywords, 26–27
Knowledge transfer, making use of comments to help in, 285
Kubernetes, 13, 184, 276, 306, 308
L
Lambdas, 322
Language design, 4
Latency, optimization of, 271
Learning curve, 305
Line separator key, 25
Local variables, 43–44
Lock-free algorithms, making use of, 272
log.Panicf, 21
Logical operators, 53
Loop control statements in Go language, 219
break statement, 220
continue statement, 221
goto statement, 220–221
Scanner, 24
Secure coding, GoLang for, 273
being cautious of mistakes and logs, 282–283
Contrast Go agent, 276
HTML templates, making use of, 278
input entries, verifying, 277
instrument-based security work, 275–276
making HTTPS communication mandatory, 280–282
need for integrated, automated security, 274–275
sensitive data, encrypting, 279–280
SQL injections, protecting ourselves against, 278–279
stuck in a rut with manual security, 273–274
Sensitive data, encrypting, 279–280
Server, development of GoLang on, 317
Shared resources
using atomic counters to get access to, 209–210
using mutual exclusion to get access to, 207–209
Shorthand declaration, 48–49, 66–67
Short variable declaration, using, 35–38
Signed integers, 28–29
Simplicity of GoLang, 7, 307
Slice composite literal, 77
Slices, 70, 320
components, 71–72
creating and beginning, 72
array, using, 72–73
existing slice, using, 73–74
make() function, using, 74–75
slice literal, using, 72
declaration, 70–71
Ints, 78
IntsAreSorted, 78–79
iterating over, 75
for loop, using, 75
for loop, using a Blank Identifier in, 76
for loop, using range in, 76
Split() function, 81–82
Trim() function, 79–81
SoundCloud, 308
Speedy scripting language, Go as, 303
Split() function, 81–82
SplitAfter, 91–92
SplitAfterN, 92–93
Split function, 90–91
SQL injections, protecting ourselves against, 278–279
Stack Overflow Developer Survey, 4
Standard library, 304
Startups, GoLang as suitable choice for, 306
Static analysis, tools for, 304
Static typing, 4, 304
Strengths of GoLang, 310
String, 24–25, 30–31, 82, 320
errors caused by, 141–142
literals, 83
backticks(“), using, 83–84
double quotes (“”), using, 83
splitting, 90
Split, 90–91
SplitAfter, 91–92
SplitAfterN, 92–93
techniques to compare, 247
Compare() method, using, 249–250
comparison operators, making use of, 247–249
trimming, 84
trim, 84–85
TrimLeft, 85–86
TrimPrefix, 89
TrimRight, 86–87
TrimSpace, 87–88
TrimSuffix, 88–89
StringBuffer, 273
StringBuilder, 273
StringCalculator function, 133
String data type, 225
String literals, 41–42
Struct, 105–106, 114, 326, 327
Struct type receiver, GoLang method with, 116
Structures, in GoLang, 112–114
Studying Go, 4
Subtract function, 128, 129
Superiority of GoLang to other computer languages, 5, 306–307, 309
for app development, 312–313
built-in testing and profiling framework, 7
for business, 311
core capability of GoLang, 5–6
hardware, Go empowering, 6–7
learning curve, 7–8
multithreading and concurrency, 6
simplicity of Go, 7
Switch statement, 63, 321–322
expression switch, 63–64
type switch, 64–65
Syntax, 25, 284
assigning data types to newly created variables, 284
avoid nesting by dealing with mistakes first, 285
comments, 25–26
Go code organization, 286
identifiers, 26
keywords, 26–27
knowledge transfer, making use of comments to help in, 285
line separator key, 25
proper naming conventions, following, 284
simple, readable, and maintainable code, 285
tokens, 25
type switch, using, 285–286
unnecessary variables and packages, eliminating, 284
utility repeats, avoiding, 285
whitespace, 27
Wages, 306
WaitGroup, 325
Walmart Labs, 311
Web, applications of GoLang for, 316–317
Web application to generate QR code in GoLang, 289
development, 290
Generator.html source code, 291
Main.go source code, 290–291
implementation, 291
necessary package, installing, 290
Website, creating, 313
While loop, 322
Whitespace, 27
Windows, installing Go on, 8
determining preinstalled version, 9
downloading and installing Go, 9–10
Workspace, 163–164