The purpose of this article is to give you a quick introduction to the Go language, so that you can spend about ten minutes reading through the whole article and have a preliminary knowledge of Go, and lay the foundation for further in-depth learning of Go.
This article assumes that you have no contact with Go at all. You may be a programmer who is proficient in other programming languages, or you may be a person who has no programming experience and just wants to become a programmer.
Introduction to Programming
Programming is the process of producing programs that can be executed on a computer. In this process, the programmer is the “labor”, the programming language is the tool, and the executable program is the result. The Go language is a great production tool for programmers to use in the programming production process.
What the programmer, as the “workforce”, does in this process is to use some programming language as a production tool to organize and express the pre-designed execution logic, much like a writer organizes and puts on paper the storyline designed in his or her brain in human language.
By this analogy, learning a programming language is like learning a human language, where vocabulary and syntax are the main things we learn. This article will provide a quick explanation of the main “vocabulary” and syntax forms of the Go language.
Go Introduction
Go is a new back-end programming language developed by three great programmers at Google, Robert Griesemer, Rob Pike and Ken Thompson, in 2007, and in 2009, Go was announced as open source.
Golang features easy-to-learn, static typing, fast compilation, efficient operation, clean code, and native support for concurrent programming. It also supports automatic memory management, which allows developers to focus more on programming itself without worrying about memory leaks. In addition, Golang also supports multi-core processors, which can better take advantage of the advantages of multi-core processors and improve the running efficiency of programs.
After more than a decade of development, Go is now a popular programming language that can be used to develop a variety of applications, including web applications, web services, system management tools, mobile applications, game development, database management, etc. Go is commonly used to build large distributed systems and to build high-performance server-side applications. Go has developed a number of “killer” applications for the current era of cloud-native computing, including Docker, Kubernetes, Prometheus, InfluxDB, Cillum, and others.
Installing Go
Go is a static language that needs to be compiled and then executed, so before we can develop Go programs, we first need to install the Go compiler and the associated toolchain. The steps to install are simple.
- Download the latest version of Golang installer from Go official website - https://go.dev/dl/
- Unzip the installation package and copy it to the location where you want to install it, e.g.
/usr/local/go
; for Windows, MacOS platforms, you can also download the installation package for graphical installation. - Setting environment variables to add the Golang installation path to the PATH variable.
- Open a terminal, type
go version
, and check if the Go language is installed successfully. If the output is similar to the following, the installation is successful!
First Go program: Hello World
Create a new directory and create a new file helloworld.go
in it. Open helloworld.go
with any editor and type the following Go source code.
Go supports running a source file directly.
But usually we compile this source file (helloworld.go) first to generate an executable binary (./helloworld) and then run it.
Go Packages
A Go package organizes a set of Go language source files into a reusable unit for use in other Go programs. All source files belonging to the same Go package are placed in a single directory, and by convention the name of that directory is the same as the package name. Take the io package of the Go standard library as an example, the list of source files in its package is as follows.
Go packages are also the basic unit of Go compilation. The Go compiler can compile packages as executable files (if the package is a main package and contains a main function implementation) or as reusable library files (.a).
Package declarations
Go packages are usually declared at the beginning of each Go source file, using the keyword package.
Package names by convention are usually single words or abbreviations in all lowercase, such as io, net, os, fmt, strconv, bytes, etc.
Importing a Go package
To use an existing Go package, we need to import the package in the source code. To import a Go package, you can use the import keyword, for example.
|
|
Go modules
Go module (module) is a new feature introduced in version 1.11 of the Go language. A Go module is a collection of related Go packages that are treated as a separate unit for uniform version management.
Go module is a new dependency management mechanism that makes it easier for developers to manage dependencies in Go language projects and allows better support for multiple versions of dependencies. In Go projects of practical value, we use Go modules for dependency management; Go modules are versioned, and Go module versioning dependencies are based on strict adherence to semantic versioning (semver).
Go uses the go.mod file to accurately document dependency requirements. Here’s how to manipulate dependencies in go.mod.
|
|
Go Minimum Project Structure
Go does not officially specify a standard structure layout for Go projects. Here is the Go minimum project structure recommended by Russ Cox, technical lead of the Go core team.
or
Variables
Golang has two types of variable declarations.
-
Using the var keyword
The declaration using the var keyword is suitable for all situations.
1 2 3 4 5 6 7 8 9
var a int // Declare an int variable a with an initial value of 0 var b int = 5 // Declare an int variable b with an initial value of 5 var c = 6 // Go will automatically assign a default type to the variable c based on the value on the right, the default integer type is int var ( // We can place variable declarations uniformly in a var block, which is equivalent to the way they were declared above. a int b int = 5 c = 6 )
Note: Go variable declarations use variable names first and types second, which is quite different from static programming languages such as C, C++, and Java.
-
Declare variables using short declarations
1 2
a := 5 // Declare a variable a. Go will automatically assign a default type to the variable a based on the value on the right, and the default integer type is int s := "hello" // Declare a variable s. Go will automatically assign a default type to the variable s based on the value on the right, and the default string type is string
Note: This declaration is limited to use within functions or methods and cannot be used to declare package level variables or global variables.
Constants
Constants in Go language are declared using the const keyword.
|
|
Types
Golang has a variety of built-in basic and composite types.
Basic types
The basic types natively supported by Go include boolean, numeric (integer, floating point, complex), and string types, here are some examples.
|
|
We can use the predefined function complex to construct the complex type, for example: complex(1.0, -1.4) constructs the complex number as 1 - 1.4i.
Composite types
Golang natively supports composite types including arrays, slices, structures, pointers, functions, interfaces, maps, and channels.
Array type
An array type is a continuum of elements of the same type, which has a fixed length and cannot be dynamically expanded.
The length of the array can be obtained with the predefined function len
.
Use the array index (starting from 0) to directly access any element in the array.
Go supports declaring multidimensional arrays, i.e., the element type of the array is still the array type.
|
|
Slice Type
A slice type is similar to an array type in that it is also a continuum of elements of the same type. The difference is that the slice type has variable length and we do not need to pass the length attribute when declaring the slice type.
The current length of the slice can be obtained with the predefined function len
.
The slice has another property, which is capacity, and its capacity value can be obtained through the predefined function cap
.
|
|
Like arrays, slices use indexes to directly access the elements in them.
Structure Types
Go’s struct type is an aggregate of different types of fields that provides a generic, aggregated abstraction of entity objects. The following is a structure type containing three fields.
We usually give a name to such a structure type, such as Person
below.
A variable of type Person is declared below.
We can access the fields in the structure via p.FieldName
.
The definition of a structure type T
can contain field members of type *T
, but cannot recursively contain field members of type T
.
Go structs can also have other types embedded in their definitions.
The name of the embedded type will be used as the field name.
Go supports empty structs that do not contain any fields.
The empty struct type has a size of 0, which is useful in many scenarios (eliminating the overhead of memory allocation).
Pointer type
The pointer type for int
type is *int
, and the pointer type for T
type is *T
. Unlike non-pointer types, pointer type variables store the address of a memory cell, and the size of *T
pointer type variables is not related to the size of the T
type, but to the length of the system address representation.
|
|
map types
map is an abstract data type provided by Go language which represents a set of unordered key-value pairs, a set of map types are defined below.
We can create an instance of a map type with a map literal or make
.
|
|
The method of manipulating map variables is also very simple.
Other types
The function, interface, and channel types are described in detail later.
Custom types
Custom types can be implemented using the type
keyword.
|
|
Go also supports defining aliases for types, in the following form.
Type conversions
Go does not support implicit auto-transformation. To perform a type conversion operation, we must do it explicitly, even if the underlying types of the two types are the same.
Many of Go’s native types support interconversion.
|
|
Control Statements
Go provides common control statements, including conditional branching (if), looping statements (for), and selective branching statements (switch).
Conditional branch statements
|
|
Loop statement
|
|
Select branch statement
|
|
Functions
Go uses the func
keyword to declare a function.
A function consists of a function name, a list of optional arguments, and a list of return values. go functions support the return of multiple return values, and we usually place the return type indicating the error value at the end of the return value list.
Functions are first-class citizens in Go, so functions themselves can be used as parameters or return values.
An anonymous function such as func(x int) int defined in the MultiplyN function above, whose implementation references its outer function MultiplyN with parameter n, is also known as a closure.
When we talk about functions, we cannot fail to mention defer, a function F called with defer in front of it, the execution of the function F will be “postponed” until the end of its caller A.
The output of the above example is as follows.
It is possible to use defer multiple times in a function.
The functions modified by defer will be called in the order of “first in, last out” at the end of the B function. The output of the above B function after execution is as follows.
Methods
Methods are functions with receivers. Here is a method Length
of type Point.
The part between the func keyword and the function name is the receiver, which is also the link between the Length method and the Point type. We can call the Length method from the Point type variable.
It is also possible to use methods as functions.
Interfaces
An interface is a collection of methods that represents a “contract”. The following is the interface type of a collection of methods consisting of three methods.
Go promotes interface-oriented programming because through interfaces we can easily build low-coupling applications.
Go also supports nesting other interface types (e.g. io.Writer
, sync.Locker
) within an interface type (e.g. I), with the result that the set of methods of the new interface type I is the concatenation of the set of its methods with the set of methods of the embedded interface types Writer
and Locker
.
Interface implementation
If a type T implements all the methods in the method collection of some interface type MyInterface, then we say that the type T implements the interface MyInterface, and so the variable t of type T can be assigned to the variable i of the interface type MyInterface, at which point the dynamic type of the variable i is T.
The methods of T can be called by the above variable i.
An interface type with an empty set of methods, interface{}
, is called an “empty interface type”, and a blank “contract” means that any type implements the empty interface type, i.e., any variable can be assigned to a variable of type interface{}
. type.
Note: The new predefined identifier
any
, introduced in Go 1.18, is the equivalent type tointerface{}
.
Type Assertion for Interfaces
Go supports type assertion to extract the value of an interface variable from its dynamic type.
|
|
If the dynamic type of the interface variable i is indeed T, then v will be given the value of that dynamic type with an ok value of true; otherwise, v will be a zero value of type T with an ok value of false.
Type assertions also support the following form of syntax.
|
|
However, in this form, the statement will throw a panic once the interface variable i has been previously given a value other than a value of type T.
Type switch for interface types
“type switch” is a special use of the switch statement, used only for interface type variables.
|
|
The switch keyword is followed by the expression x.(type)
. This form of expression is exclusive to switch statements and can only be used in switch statements. The x in this expression must be an interface type variable, and the result of the expression is the dynamic type corresponding to the interface type variable.
The expression after switch in the above example can also be changed from x.(type)
to v := x.(type)
. v will store information about the value corresponding to the dynamic type of variable x.
Generic
Go supports generics since version 1.18. The basic syntax of Go generics is type parameter (type parameter), and the essence of the Go generic scheme is the support for type parameters, including the following.
- generic function: a function with type parameters.
- generic type: a custom type with type arguments.
- generic method: a method of a generic type.
Generic functions
The following is the definition of a generic function max.
Compared with ordinary Go functions, the max function has an extra code enclosed by square brackets between the function name and the function argument list: [T ordered]; the argument type in the max argument list and the return value type in the return value list are both T, not a specific type.
The extra [T ordered] in the max function is the list of type parameters (type parameters list) of the Go generic, and in the example there is only one type parameter T in this list, ordered is the type constraint of the type parameter (type constraint).
We can call generic functions like normal functions, and we can explicitly specify the actual parameter type of the generic.
Go also supports automatic inference of the actual parameter type of a generic type.
Generic types
A generic type is a Go type with a type parameter in the type declaration.
Take the generic type Set as an example, its usage is as follows.
Generic methods
Go types can have their own methods, and generic types are no exception. The methods defined for generic types are called generic methods.
|
|
Type constraints
Go sets restrictions on the type parameters of generic functions and the implementation code in generic functions by means of type constraints (constraint). go uses the extended syntax post-interface type to define constraints.
The following is an example of using a regular interface type as a constraint
The Go interface type declaration syntax has been extended to support putting type element information in the interface type.
|
|
Concurrency
Go does not use OS threads as the basic execution unit for concurrency. Instead, it implements goroutine, a lightweight user-level thread that is scheduled by the Go runtime (runtime), to provide native support for concurrent programming.
goroutine
We can create a goroutine by using the go keyword + function/method. once created, the new goroutine will have a separate code execution flow and will be dispatched by the Go runtime along with the goroutine that created it.
After the goroutine’s execution function returns, the goroutine exits. If the main goroutine (the goroutine that executes main.main) exits, then the entire Go application process will exit and the program life cycle will end.
channel
Go provides a native mechanism for communication between goroutines, channel, which is defined and operated as follows.
|
|
Here is an example of two goroutines communicating based on a channel.
Go provides the select mechanism when it comes to operating on multiple channels at the same time. With select, we can send/receive operations on multiple channels at the same time.
|
|
Error handling
Go provides a simple, error handling mechanism based on the comparison of error values. This mechanism makes it mandatory for every developer to explicitly attend to and handle each error.
error type
Go uses the interface type error to represent errors, and by convention we usually put the error type return value at the end of the return value list.
Any instance of a type that implements the Error method of error can be assigned as an error value to the error interface variable.
Go provides convenient methods for constructing error values.
Error handling forms
The most common forms of error handling for Go are as follows.
Usually we define some “sentinel” error values to assist the error handler in inspecting the error values and making decisions on error handling branches.
|
|
Is and As
Starting with Go 1.13, the standard library errors package provides the Is function for error handler review of error values. the Is function is similar to comparing an error type variable to a “sentinel” error value.
The difference is that if the underlying error value of the error type variable is a Wrapped Error, the errors.Is
method goes down the Error Chain where the Wrapped Error is located and compares it with all the Wrapped Errors in the chain until it finds a match.
The standard library errors package also provides the As function for error handlers to review error values. as function is similar to determining whether an error type variable is a specific custom error type by type assertion.
If the dynamic error value of the error type variable is a wrapped error, the errors.As
function goes down the error chain where the wrapped error is located and compares it with the types of all the wrapped errors in the chain until it finds a matching error type, just as the errors.Is
function does.
Summary
By reading this, you have an entry-level knowledge of the Go language, but further study and practice is needed to become a Gopher (the name given to Go developers).
Ref
https://tonybai.com/2023/02/23/learn-go-in-10-min/