Learning Go Programming

4.6 (5 reviews total)
By Vladimir Vivien
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. A First Step in Go

About this book

The Go programming language has firmly established itself as a favorite for building complex and scalable system applications. Go offers a direct and practical approach to programming that let programmers write correct and predictable code using concurrency idioms and a full-featured standard library.

This is a step-by-step, practical guide full of real world examples to help you get started with Go in no time at all. We start off by understanding the fundamentals of Go, followed by a detailed description of the Go data types, program structures and Maps. After this, you learn how to use Go concurrency idioms to avoid pitfalls and create programs that are exact in expected behavior. Next, you will be familiarized with the tools and libraries that are available in Go for writing and exercising tests, benchmarking, and code coverage.

Finally, you will be able to utilize some of the most important features of GO such as, Network Programming and OS integration to build efficient applications. All the concepts are explained in a crisp and concise manner and by the end of this book; you would be able to create highly efficient programs that you can deploy over cloud.

Publication date:
October 2016
Publisher
Packt
Pages
348
ISBN
9781784395438

 

Chapter 1. A First Step in Go

In the first chapter of the book, you will be introduced to Go and take a tour of the features that have made the language a favorite among its adopters. The start of the chapter provides the motivation behind the Go programming language. If you are impatient, however, you are welcome to skip to any of the other topics and learn how to write your first Go program. Finally, the Go in a nutshell section provides a high-level summary of the characteristics of the language.

The following topics are covered in this chapter:

  • The Go programming language

  • Playing with Go

  • Installing Go

  • Your first Go program

  • Go in a nutshell

 

The Go programming language


Since the invention of the C language in the early 1970s by Dennis Ritchie at Bell Labs, the computing industry has produced many popular languages that are based directly on (or have borrowed ideas from) its syntax. Commonly known as the C-family of languages, they can be split into two broad evolutionary branches. In one branch, derivatives such as C++, C#, and Java have evolved to adopt a strong type system, object orientation, and the use of compiled binaries. These languages, however, tend to have a slow build-deploy cycle and programmers are forced to adopt a complex object-oriented type system to attain runtime safety and speed of execution:

In the other evolutionary linguistic branch are languages such as Perl, Python, and JavaScript that are described as dynamic languages for their lack of type safety formalities, use of lightweight scripting syntax, and code interpretation instead of compilation. Dynamic languages have become the preferred tool for web and cloud scale development where speed and ease of deployment are valued over runtime safety. The interpreted nature of dynamic languages means, however, they generally run slower than their compiled counterparts. In addition, the lack of type safety at runtime means the correctness of the system scales poorly as the application grows.

Go was created as a system language at Google in 2007 by Robert Griesemer, Rob Pike, and Ken Thomson to handle the needs of application development. The designers of Go wanted to mitigate the issues with the aforementioned languages while creating a new language that is simple, safe, consistent, and predictable. As Rob Pike puts it:

"Go is an attempt to combine the safety and performance of a statically-typed language with the expressiveness and convenience of a dynamically-typed interpreted language."

Go borrows ideas from different languages that came before it, including:

  • Simplified but concise syntax that is fun and easy to use

  • A type of system that feels more like a dynamic language

  • Support for object-oriented programming

  • Statically typed for compilation and runtime safety

  • Compiled to native binaries for fast runtime execution

  • Near-zero compilation time that feels more like an interpreted language

  • A simple concurrency idiom to leverage multi-core, multi-chip machines

  • A garbage collector for safe and automatic memory management

The remainder of this chapter will walk you through an introductory set of steps that will give you a preview of the language and get you started with building and running your first Go program. It is a precursor to the topics that are covered in detail in the remaining chapters of the book. You are welcome to skip to other chapters if you already have a basic understanding of Go.

 

Playing with Go


Before we jump head-first into installing and running Go tools on your local machine, let us take a look at the Go Playground. The creators of the language have made available a simple way to familiarize yourself with the language without installing any tools. Known as the Go Playground, it is a web-based tool, accessible from https://play.golang.org/, that uses an editor metaphor to let developers test their Go skills by writing code directly within the web browser window. The Playground gives its users the ability to compile and run their code on Google's remote servers and get immediate results as shown in the following screenshot:

The editor is basic, as it is meant to be used as a learning tool and a way to share code with others. The Playground includes practical features such as line numbers and formatting to ensure your code remains readable as it goes beyond a few lines long. Since this is a free service that consumes real compute resources, Google understandably imposes a few limitations on what can be done with Playground:

  • You are restricted on the amount of memory your code will consume

  • Long-running programs will be killed

  • Access to files is simulated with an in-memory filesystem.

  • Network access is simulated against the loopback interface only

No IDE required

Besides the Go Playground, how is one supposed to write Go code anyway? Writing Go does not require a fancy Integrated Development Environment (IDE). As a matter of fact, you can get started writing your simple Go programs with your favorite plain text editor that is bundled with your OS. There are, however, Go plugins for most major text editors (and full-blown IDEs) such as Atom, Vim, Emacs, Microsoft Code, IntelliJ, and many others. There is a complete list of editors and IDE plugins for Go which can be found at https://github.com/golang/go/wiki/IDEsAndTextEditorPlugins.

Installing Go

To start programming with Go on your local machine you will need to install the Go Toolchain on your computer. At the time of writing, Go comes ready to be installed on the following major OS platforms:

  • Linux

  • FreeBSD Unix

  • Mac OSX

  • Windows

The official installation packages are all available for 32-bit and 64-bit Intel-based architectures. There are also official binary releases that are available for ARM architectures as well. As Go grows in popularity, there will certainly be more binary distribution choices made available in the future.

Let us skip the detailed installation instructions as they will certainly change by the time you read this. Instead, you are invited to visit http://golang.org/doc/install and follow the directions given for your specific platform. Once completed, be sure to test your installation is working before continuing to use the following command:

$> go version
go version go1.6.1 linux/amd64

The previous command should print the version number, target OS, and the machine architecture where Go and its tools are installed. If you do not get an output similar to that preceding command, ensure to add the path of the Go binaries to your OS's execution PATH environment variable.

Before you start writing your own code, ensure that you have properly set up your GOPATH. This is a local directory where your Go source files and compiled artifacts are saved as you use the Go Toolchain. Follow the instructions found in https://golang.org/doc/install#testing to set up your GOPATH.

Source code examples

The programming examples presented throughout this book are available on the GitHub source code repository service. There you will find all source files grouped by chapters in the repository at https://github.com/vladimirvivien/learning-go/. To save the readers a few keystrokes, the examples use a shortened URL, that starts with golang.fyi, that points directly to the respective file in GitHub.

Alternatively, you can follow along by downloading and unzipping (or cloning) the repository locally. Create a directory structure in your GOPATH so that the root of the source files is located at $GOPATH/src/github.com/vladimirvivien/learning-go/.

 

Your first Go program


After installing the Go tools successfully on your local machine, you are now ready to write and execute your first Go program. For that, simply open your favorite text editor and type in the simple Hello World program shown in the following code:

package main
import "fmt"
func main() { 
  fmt.Println("Hello, World!")
} 

golang.fyi/ch01/helloworld.go

Save the source code in a file called helloworld.go anywhere inside your GOPATH. Then use the following Go command to compile and run the program:

$> go run helloworld.go 
Hello, World!

If all goes well, you should see the message Hello, World! output on your screen. Congratulations, you have just written and executed your first Go program. Now, let us explore the attributes and characteristics of the Go language at a high level.

 

Go in a nutshell


By design, Go has a simple syntax. Its designers wanted to create a language that is clear, concise, and consistent with few syntactic surprises. When reading Go code, keep this mantra in mind: what you see is what it is. Go shies away from a clever and terse coding style in favor of code that is clear and readable as exemplified by the following program:

// This program prints molecular information for known metalloids 
// including atomic number, mass, and atom count found 
// in 100 grams of each element using the mole unit. 
// See http://en.wikipedia.org/wiki/Mole_(unit) 
package main 
 
import "fmt" 
 
const avogadro float64 = 6.0221413e+23 
const grams = 100.0 
 
type amu float64 
 
func (mass amu) float() float64 { 
  return float64(mass) 
} 
 
type metalloid struct { 
  name   string 
  number int32 
  weight amu 
} 
 
var metalloids = []metalloid{ 
  metalloid{"Boron", 5, 10.81}, 
  metalloid{"Silicon", 14, 28.085}, 
  metalloid{"Germanium", 32, 74.63}, 
  metalloid{"Arsenic", 33, 74.921}, 
  metalloid{"Antimony", 51, 121.760}, 
  metalloid{"Tellerium", 52, 127.60}, 
  metalloid{"Polonium", 84, 209.0}, 
} 
 
// finds # of moles 
func moles(mass amu) float64 { 
  return float64(mass) / grams 
} 
 
// returns # of atoms moles 
func atoms(moles float64) float64 { 
  return moles * avogadro 
} 
 
// return column headers 
func headers() string { 
  return fmt.Sprintf( 
    "%-10s %-10s %-10s Atoms in %.2f Grams\n", 
    "Element", "Number", "AMU", grams, 
  ) 
} 
 
func main() { 
  fmt.Print(headers()) 
 
    for _, m := range metalloids { 
      fmt.Printf( 
    "%-10s %-10d %-10.3f %e\n", 
      m.name, m.number, m.weight.float(), atoms(moles(m.weight)), 
      ) 
    } 
}

golang.fyi/ch01/metalloids.go

When the code is executed, it will give the following output:

$> go run metalloids.go 
Element    Number     AMU        Atoms in 100.00 Grams 
Boron      5          10.810     6.509935e+22 
Silicon    14         28.085     1.691318e+23 
Germanium  32         74.630     4.494324e+23 
Arsenic    33         74.921     4.511848e+23 
Antimony   51         121.760    7.332559e+23 
Tellerium  52         127.600    7.684252e+23 
Polonium   84         209.000    1.258628e+24

If you have never seen Go before, you may not understand some of the details of the syntax and idioms used in the previous program. Nevertheless, when you read the code, there is a good chance you will be able to follow the logic and form a mental model of the program's flow. That is the beauty of Go's simplicity and the reason why so many programmers use it. If you are completely lost, no need to worry, as the subsequent chapters will cover all aspects of the language to get you going.

Functions

Go programs are composed of functions, the smallest callable code unit in the language. In Go, functions are typed entities that can either be named (as shown in the previous example) or be assigned to a variable as a value:

// a simple Go function 
func moles(mass amu) float64 { 
    return float64(mass) / grams 
} 

Another interesting feature about Go functions is their ability to return multiple values as a result of a call. For instance, the previous function could be re-written to return a value of type error in addition to the calculated float64 value:

func moles(mass amu) (float64, error) { 
    if mass < 0 { 
        return 0, error.New("invalid mass") 
    } 
    return (float64(mass) / grams), nil 
}

The previous code uses the multi-return capabilities of Go functions to return both the mass and an error value. You will encounter this idiom throughout the book used as a mean to properly signal errors to the caller of a function. There will be further discussion on multi-return value functions covered in Chapter 5, Functions in Go.

Packages

Source files containing Go functions can be further organized into directory structures known as a package. Packages are logical modules that are used to share code in Go as libraries. You can create your own local packages or use tools provided by Go to automatically pull and use remote packages from a source code repository. You will learn more about Go packages in Chapter 6, Go Packages and Programs.

The workspace

Go follows a simple code layout convention to reliably organize source code packages and to manage their dependencies. Your local Go source code is stored in the workspace, which is a directory convention that contains the source code and runtime artifacts. This makes it easy for Go tools to automatically find, build, and install compiled binaries. Additionally, Go tools rely on the workspace setup to pull source code packages from remote repositories, such as Git, Mercurial, and Subversion, and satisfy their dependencies.

Strongly typed

All values in Go are statically typed. However, the language offers a simple but expressive type system that can have the feel of a dynamic language. For instance, types can be safely inferred as shown in the following code snippet:

const grams = 100.0 

As you would expect, constant grams would be assigned a numeric type, float64, to be precise, by the Go type system. This is true not only for constants, but any variable can use a short-hand form of declaration and assignment as shown in the following example:

package main  
import "fmt"  
func main() { 
  var name = "Metalloids" 
  var triple = [3]int{5,14,84} 
  elements := []string{"Boron","Silicon", "Polonium"} 
  isMetal := false 
  fmt.Println(name, triple, elements, isMetal) 
 
} 

Notice that the variables, in the previous code snippet, are not explicitly assigned a type. Instead, the type system assigns each variable a type based on the literal value in the assignment. Chapter 2, Go Language Essentials and Chapter 4, Data Types, go into more details regarding Go types.

Composite types

Besides the types for simple values, Go also supports composite types such as array, slice, and map. These types are designed to store indexed elements of values of a specified type. For instance, the metalloid example shown previously makes use of a slice, which is a variable-sized array. The variable metalloid is declared as a slice to store a collection of the type metalloid. The code uses the literal syntax to combine the declaration and assignment of a slice of type metalloid:

var metalloids = []metalloid{ 
    metalloid{"Boron", 5, 10.81}, 
    metalloid{"Silicon", 14, 28.085}, 
    metalloid{"Germanium", 32, 74.63}, 
    metalloid{"Arsenic", 33, 74.921}, 
    metalloid{"Antimony", 51, 121.760}, 
    metalloid{"Tellerium", 52, 127.60}, 
    metalloid{"Polonium", 84, 209.0}, 
} 

Go also supports a struct type which is a composite that stores named elements called fields as shown in the following code:

func main() { 
  planet := struct { 
      name string 
      diameter int  
  }{"earth", 12742} 
} 

The previous example uses the literal syntax to declare struct{name string; diameter int} with the value {"earth", 12742}. You can read all about composite types in Chapter 7, Composite Types.

The named type

As discussed, Go provides a healthy set of built-in types, both simple and composite. Go programmers can also define new named types based on an existing underlying type as shown in the following snippet extracted from metalloid in the earlier example:

type amu float64 
 
type metalloid struct { 
  name string 
  number int32 
  weight amu 
} 

The previous snippet shows the definition of two named types, one called amu, which uses type float64 as its underlying type. Type metalloid, on the other hand, uses a struct composite type as its underlying type, allowing it to store values in an indexed data structure. You can read more about declaring new named types in Chapter 4, Data Types.

Methods and objects

Go is not an object-oriented language in a classical sense. Go types do not use a class hierarchy to model the world as is the case with other object-oriented languages. However, Go can support the object-based development idiom, allowing data to receive behaviors. This is done by attaching functions, known as methods, to named types.

The following snippet, extracted from the metalloid example, shows the type amu receiving a method called float() that returns the mass as a float64 value:

type amu float64 
 
func (mass amu) float() float64 { 
    return float64(mass) 
} 

The power of this concept is explored in detail in Chapter 8, Methods, Interfaces, and Objects.

Interfaces

Go supports the notion of a programmatic interface. However, as you will see in Chapter 8, Methods, Interfaces, and Objects, the Go interface is itself a type that aggregates a set of methods that can project capabilities onto values of other types. Staying true to its simplistic nature, implementing a Go interface does not require a keyword to explicitly declare an interface. Instead, the type system implicitly resolves implemented interfaces using the methods attached to a type.

For instance, Go includes the built-in interface called Stringer, defined as follows:

type Stringer interface { 
    String() string 
} 

Any type that has the method String() attached, automatically implements the Stringer interface. So, modifying the definition of the type metalloid, from the previous program, to attach the method String() will automatically implement the Stringer interface:

type metalloid struct { 
    name string 
    number int32 
    weight amu 
} 
func (m metalloid) String() string { 
  return fmt.Sprintf( 
    "%-10s %-10d %-10.3f %e", 
    m.name, m.number, m.weight.float(), atoms(moles(m.weight)), 
  ) 
}  

golang.fyi/ch01/metalloids2.go

The String() methods return a pre-formatted string that represents the value of a metalloid. The function Print(), from the standard library package fmt, will automatically call the method String(), if its parameter implements stringer. So, we can use this fact to print metalloid values as follow:

func main() { 
  fmt.Print(headers()) 
  for _, m := range metalloids { 
    fmt.Print(m, "\n") 
  } 
} 

Again, refer to Chapter 8, Methods, Interfaces, and Objects, for a thorough treatment of the topic of interfaces.

Concurrency and channels

One of the main features that has rocketed Go to its current level of adoption is its inherent support for simple concurrency idioms. The language uses a unit of concurrency known as a goroutine, which lets programmers structure programs with independent and highly concurrent code.

As you will see in the following example, Go also relies on a construct known as a channel used for both communication and coordination among independently running goroutines. This approach avoids the perilous and (sometimes brittle) traditional approach of thread communicating by sharing memory. Instead, Go facilitates the approach of sharing by communicating using channels. This is illustrated in the following example that uses both goroutines and channels as processing and communication primitives:

// Calculates sum of all multiple of 3 and 5 less than MAX value. 
// See https://projecteuler.net/problem=1 
package main 
 
import ( 
  "fmt" 
) 
 
const MAX = 1000 
 
func main() { 
  work := make(chan int, MAX) 
  result := make(chan int) 
 
  // 1. Create channel of multiples of 3 and 5 
  // concurrently using goroutine 
  go func(){ 
    for i := 1; i < MAX; i++ { 
      if (i % 3) == 0 || (i % 5) == 0 { 
        work <- i // push for work 
      } 
    } 
    close(work)  
  }() 
 
  // 2. Concurrently sum up work and put result 
  //    in channel result  
  go func(){ 
    r := 0 
    for i := range work { 
      r = r + i 
    } 
    result <- r 
  }() 
 
  // 3. Wait for result, then print 
  fmt.Println("Total:", <- result) 
} 

golang.fyi/ch01/euler1.go

The code in the previous example splits the work to be done between two concurrently running goroutines (declared with the go keyword) as annotated in the code comment. Each goroutine runs independently and uses the Go channels, work and result, to communicate and coordinate the calculation of the final result. Again, if this code does not make sense at all, rest assured, concurrency has the whole of Chapter 9, Concurrency, dedicated to it.

Memory management and safety

Similar to other compiled and statically-typed languages such as C and C++, Go lets developers have direct influence on memory allocation and layout. When a developer creates a slice (think array) of bytes, for instance, there is a direct representation of those bytes in the underlying physical memory of the machine. Furthermore, Go borrows the notion of pointers to represent the memory addresses of stored values giving Go programs the support of passing function parameters by both value and reference.

Go asserts a highly opinionated safety barrier around memory management with little to no configurable parameters. Go automatically handles the drudgery of bookkeeping for memory allocation and release using a runtime garbage collector. Pointer arithmetic is not permitted at runtime; therefore, developers cannot traverse memory blocks by adding to or subtracting from a base memory address.

Fast compilation

Another one of Go's attractions is its millisecond build-time for moderately-sized projects. This is made possible with features such as a simple syntax, conflict-free grammar, and a strict identifier resolution that forbids unused declared resources such as imported packages or variables. Furthermore, the build system resolves packages using transitivity information stored in the closest source node in the dependency tree. Again, this reduces the code-compile-run cycle to feel more like a dynamic language instead of a compiled language.

Testing and code coverage

While other languages usually rely on third-party tools for testing, Go includes both a built-in API and tools designed specifically for automated testing, benchmarking, and code coverage. Similar to other features in Go, the test tools use simple conventions to automatically inspect and instrument the test functions found in your code.

The following function is a simplistic implementation of the Euclidean division algorithm that returns a quotient and a remainder value (as variables q and r) for positive integers:

func DivMod(dvdn, dvsr int) (q, r int) { 
  r = dvdn 
  for r >= dvsr { 
    q += 1 
    r = r - dvsr 
  } 
  return 
} 

golang.fyi/ch01/testexample/divide.go

In a separate source file, we can write a test function to validate the algorithm by checking the remainder value returned by the tested function using the Go test API as shown in the following code:

package testexample 
import "testing" 
func TestDivide(t *testing.T) { 
  dvnd := 40 
    for dvsor := 1; dvsor < dvnd; dvsor++ { 
      q, r := DivMod(dvnd, dvsor) 
  if (dvnd % dvsor) != r { 
    t.Fatalf("%d/%d q=%d, r=%d, bad remainder.", dvnd, dvsor, q, r) 
    } 
  } 
}  

golang.fyi/ch01/testexample/divide_test.go

To exercise the test source code, simply run Go's test tool as shown in the following example:

$> go test . 
ok   github.com/vladimirvivien/learning-go/ch01/testexample  0.003s

The test tool reports a summary of the test result indicating the package that was tested and its pass/fail outcome. The Go Toolchain comes with many more features designed to help programmers create testable code, including:

  • Automatically instrument code to gather coverage statistics during tests

  • Generating HTML reports for covered code and tested paths

  • A benchmark API that lets developers collect performance metrics from tests

  • Benchmark reports with valuable metrics for detecting performance issues

You can read all about testing and its related tools in Chapter 12, Code Testing.

Documentation

Documentation is a first-class component in Go. Arguably, the language's popularity is in part due to its extensive documentation (see http://golang.org/pkg). Go comes with the Godoc tool, which makes it easy to extract documentation from comment text embedded directly in the source code. For example, to document the function from the previous section, we simply add comment lines directly above the DivMod function as shown in the following example:

// DivMod performs a Eucledan division producing a quotient and remainder. 
// This version only works if dividend and divisor > 0. 
func DivMod(dvdn, dvsr int) (q, r int) { 
... 
}

The Go documentation tool can automatically extract and create HTML-formatted pages. For instance, the following command will start the Godoc tool as a server on localhost port 6000:

$> godoc -http=":6001"

You can then access the documentation of your code directly from your web browser. For instance, the following figure shows the generated documentation snippet for the previous function located at http://localhost:6001/pkg/github.com/vladimirvivien/learning-go/ch01/testexample/:

An extensive library

For its short existence, Go rapidly grew a collection of high-quality APIs as part of its standard library that are comparable to other popular and more established languages. The following, by no means exhaustive, lists some of the core APIs that programmers get out-of-the-box:

  • Complete support for regular expressions with search and replace

  • Powerful IO primitives for reading and writing bytes

  • Full support for networking from socket, TCP/UDP, IPv4, and IPv6

  • APIs for writing production-ready HTTP services and clients

  • Support for traditional synchronization primitives (mutex, atomic, and so on)

  • General-purpose template framework with HTML support

  • Support for JSON/XML serializations

  • RPC with multiple wire formats

  • APIs for archive and compression algorithms: tar, zip/gzip, zlib, and so on

  • Cryptography support for most major algorithms and hash functions

  • Access to OS-level processes, environment info, signaling, and much more

The Go Toolchain

Before we end the chapter, one last aspect of Go that should be highlighted is its collection of tools. While some of these tools were already mentioned in previous sections, others are listed here for your awareness:

  • fmt: Reformats source code to adhere to the standard

  • vet: Reports improper usage of source code constructs

  • lint: Another source code tool that reports flagrant style infractions

  • goimports: Analyzes and fixes package import references in source code

  • godoc: Generates and organizes source code documentation

  • generate: Generates Go source code from directives stored in source code

  • get: Remotely retrieves and installs packages and their dependencies

  • build: Compiles code in a specified package and its dependencies

  • run: Provides the convenience of compiling and running your Go program

  • test: Performs unit tests with support for benchmark and coverage reports

  • oracle static analysis tool: Queries source code structures and elements

  • cgo: Generates source code for interoperability between Go and C

 

Summary


Within its relatively short existence, Go has won the hearts of many adopters who value simplicity as a way to write code that is exact and is able to scale in longevity. As you have seen from the previous sections in this chapter, it is easy to get started with your first Go program.

The chapter also exposed its readers to a high-level summary of the most essential features of Go including its simplified syntax, its emphasis on concurrency, and the tools that make Go a top choice for software engineers, creating systems for the age of data center computing. As you may imagine, this is just a taste of what's to come.

In the following chapters, the book will continue to explore in detail the syntactical elements and language concepts that make Go a great language to learn. Let's Go!

About the Author

  • Vladimir Vivien

    Vladimir Vivien (@vladimirvivien) is a software engineer living in the United States. He is a previously published author and has written code in languages such as Java, JavaScript, Python, and C to name a few. Vladimir has work in diverse industries including technology, publishing, financial, and healthcare. After years of building enterprise systems using Java, Vladimir came to Go for its simplicity and stayed for its concurrency and fast build time. Vladimir continues to use Go as his primary language to build and create open source software (https://github.com/vladimirvivien).

    Browse publications by this author

Latest Reviews

(5 reviews total)
Forklarer hvordan man kommer i gang med Go på en fin og oversiktlig måte.
Good process, fast and secure.
Good introduction to the Go language. Thanks!

Recommended For You

Mastering Go - Second Edition

Dive deep into the Go language and become an expert Go developer

By Mihalis Tsoukalos
Learn Data Structures and Algorithms with Golang

Explore Golang's data structures and algorithms to design, implement, and analyze code in the professional setting

By Bhagvan Kommadi
Go Programming Cookbook - Second Edition

Tackle the trickiest of problems in Go programming with this practical guide

By Aaron Torres
Go Standard Library Cookbook

Implement solutions by leveraging the power of the GO standard library and reducing dependency on external crates

By Radomir Sohlich