There seems to be a “two ends in one” phenomenon in the computing world.

On the one hand, more and more computing needs are being moved to the cloud (i.e., the cloud computing side). If you are a practitioner, you should be used to “deploying programs to the cloud”; on the other hand, some terminals are becoming more and more intelligent. Some manufacturers have even built neural network engines (a processing unit used to perform machine learning and artificial intelligence tasks) into their phones.

We can say that the big “intelligence” in the cloud, the small “intelligence” in the terminal form gradually formed. However, they are not fragmented, but gradually towards integration. They are working together to weave an “AI co-processing network”.

In this puzzle, the Go language is clearly positioned and advantageous in the cloud. To be more specific, Go’s main battleground is in the application layer of the cloud (e.g., Web services, middleware, etc.), but not in the underlying layer of the cloud (e.g., operating systems, drivers, etc.).

Back in the day, the Go language was claimed to be the C language of the cloud computing era. Its goal was to achieve the efficiency of running programs in C and the efficiency of developing programs in Python. So far, we can’t say that this goal has been fully achieved, but we can describe it as “very close”.

Of course, evaluating a programming language is not just about how efficiently it runs and how efficiently it develops. It’s also about the development team, the technical community, the technical ecosystem, and so on. But that’s a whole other topic.

On November 10th of this year, the Go language celebrated its 12th birthday. In terms of trends, the Go language is relatively stable in 2021, as evidenced by the TIOBE Index (a well-known programming language ranking) usage statistics for Go ( https://www.tiobe.com/tiobe-index/go/ ). This is in contrast to its ups and downs between 2010 and 2018.

image

This is confirmed by Google’s search engine trend statistics.

image

Not surprisingly, it was Chinese Go language fans who contributed the majority of searches again this year.

image

The Go language is so hot in China!

When we focus on China, we can see the cities where the most Go-language-focused people are in the country.

image

image

Please note that the hotness shown above is based on the last 12 months of data. To be honest, the cities of Jinan and Xi’an, which rank first and second here, are quite surprising to the author. Perhaps there are schools and companies in these two cities that are heavily promoting the Go language. Of course, this is just a guess, and there is no data to support it.

The ranking of the cities after them, as far as the author can tell, should be similar. Beijing has always been a popular place for technology companies. Shenzhen has also seen a rapid rise in software companies in recent years. And because of the unique charm of Chengdu, the past two years, there are many software companies to set up camp there. As for Hangzhou, Shanghai, Guangzhou also needless to say, they have been the Internet and software companies are more piled up in the place.

Let’s take a look at the relevant search terms.

image

As someone who has studied the Go language for many years, I can see here that people who want to learn or are just learning Go are the majority of people searching for this programming language. This may have something to do with the fact that Go is starting to gain popularity in colleges and universities. Of course, the fact that “more and more companies (especially the leading ones and the upstarts) are using Go” must also be an important factor. So, no matter what, the Go language still has a lot of potential in China.

Well, we took a longitudinal look at the Go language’s popularity in China. Now, let’s take a side-by-side comparison.

In addition to Go, the authors have selected a few programming languages that have more intersection with it in terms of their main areas of interest. They are C, C++, Java, and Rust, as shown in the figure below.

image

Globally, the Go language is not far behind Java, but also has a significant gap with C and C++ in terms of popularity. However, if we focus on China, we will find something else.

image

For the country, it is clear that application-level programming languages will receive more attention from developers. Go is second only to Java here. C, which is the dominant language in the underlying development, is only the second to last among the 5 programming languages. Although this article is mainly about Go, this situation makes me think - is the focus on software infrastructure too low for the majority of developers in China?

Let’s look at the 2021 Developer Survival Report ( https://insights.stackoverflow.com/survey/2021 ) published by StackOverflow (the world’s largest programming community and Q&A site). The report is global in scope. Although the participation of Chinese developers is still low, it doesn’t stop us from using it for reference.

image

In this report’s “Favorite Programming Languages” ranking, the Go language comes in at #10. It has moved up 2 places from last year. And Rust still holds the number one position, as it did last year. However, if we look back at the popularity of the Rust language in Google search engines, can we see some clues? Perhaps it has to do with the high learning curve of Rust. Across the board, the programming languages that are more beloved than Go are either charming in their features or have a large language ecosystem (or an unusually rich set of third-party libraries and development tools).

In contrast, the authors consider the following ranking of “most wanted programming languages” to be more informative for professional programmers. In this ranking, Go and Rust are very close to each other. However, they are not as good as the absolute mainstream scripting languages. Here we can also see that the ease of use of a programming language (and the efficiency of the development of programs) is crucial to the choice of developers.

image

Of course, we’re not immune to this, so let’s take a final look at the Salary and Development Experience quadrant of the report (globally).

image

This quadrant represents a result based on big data statistics. The salary in it is taken as the median annual salary of developers, while the development experience is taken as the average of the number of years developers have been in the business. However, it still has some reference value.

As we can see, the Go language still pays very well globally. This is even higher than the more novel Julia and Swift languages. The Java language, which is a strong rival of Go, is probably due to an over-saturated market and the median annual salary of the developers involved is far from that of Go.

By this point, we’ve looked at quite a few charts together. We can see that the trend of Go language is still very stable. In this regard, the situation in China is significantly better than abroad. In recent years, more and more domestic developers have started to learn and experiment with the Go language, and more and more domestic Internet companies and software companies have started to embrace and use the Go language. The author believes that the market share of Go in China is still far from the top, and there is still a lot of potential behind.

Year in Review

Now that we’ve covered the big trends, let’s talk about what’s been updated in 2021.

As a rule, the Go language is updated with a minor version in February and a minor version in August each year. In 2021, the official team released versions 1.16 and 1.17 of the Go language on time. Due to space limitations, we will only discuss the updates that seem to be the most important or significant here.

New official website

As Go language fans know, the official website of Go language is golang.org, however, due to some network reasons, domestic users have some difficulties to access this website. But after all, the Go language user community in China is very large, so the official Go language team (hereinafter referred to as Go team) launched a China-only official website some years ago: golang.google.cn.

However, in February 2019, the Go team registered a new domain name: go.dev. The .dev here is a domain extension that Google came up with on its own. Apparently, this domain name is very short and easy to remember. Since then, the Go team has been gradually consolidating the content of golang.org and related sites under go.dev, in an effort to unify the many scattered official Go components.

On the Go language’s 12th birthday, the Go team published a special blog post in which they announced a major revamp of the go.dev website. This marked the completion of the integration of content about go.dev. In addition to the main content that had been under golang.org (such as download instructions, documentation, tutorials, official blog, playground, etc.) being moved to go.dev, godoc.org, the Go package archive, has been merged into go.dev with a new secondary domain name of pkg.go.dev.

image

You can take a look at pkg.go.dev, which is now very powerful. Not only can we easily search and view Go packages (both official and third-party) and their code and documentation, but we can also quickly find out the latest version of a package, its update date, distribution protocol, module management, repository address, and more, and easily see what other packages it uses and what packages are using it. It’s no exaggeration to say that it’s enough to use it to find Go packages.

Finally, and crucially, our access to go.dev in China is currently unhindered.

Module Management

This year, the Go team has put a lot of effort into module management for the Go language. It’s safe to say that the module management features are now quite usable.

As a quick explanation, we are talking about modules in the same sense as we are talking about packages. Since the official go module mechanism was created, these two terms have been fully equivalent in the context of Go module management. They both refer to a collection of code that can be packaged and distributed for use by other programs. The only difference is that packages are more of an informal term for people, while modules are the official term.

There is a related term, package. A package is a number of source files in the same file directory, all of which have the exact same package declaration statement. Multiple packages can belong to the same module. Conversely, a module can be further divided into multiple packages according to the function, hierarchy, and other logic. Of course, a module can have only one package in it. And a package will definitely belong to a module.

So, when Go’s official module management feature comes on board, we have a multi-level code organization scheme from modules to packages (and sub-packages) to source files. Where the source file is the smallest unit of code compiled, the code package is the smallest unit of code organization, and the module is the smallest unit of code distribution. Once you understand this, you can usually get started with the Go language’s module management tools pretty quickly.

Due to the limitations of the topic, let’s put aside the module management methods and techniques and focus on the Go team’s updates in this area.

System environment variable GO111MODULE

If you have used older versions of the Go language, you will know about the GO111MODULE system environment variable. Its purpose is to make it easier for developers to switch between the original GOPATH mechanism and the new go module mechanism. This has always been the practice of the Go team.

When a new mechanism is implemented and released, they provide optional features for developers to try out for a while and improve the mechanism in the meantime. During this process, the Go team adjusts the default values of the corresponding system environment variables in new versions of Go as the mechanism matures.

For example, they initially set the default value to off (i.e., off by default). Once most of the bugs are fixed and the functionality is pretty much improved, they will set the default to auto. After the mechanism is basically mature, they will also set the default value to on (i.e., the default is on). Finally, once the mechanism has been refined, the Go team will remove the corresponding system environment variable (and the corresponding toggle) from the new version of Go, and switch to the new mechanism entirely.

The system environment variable GO111MODULE has been around for a few years now. This shows that the go module has had a few bumps in the road since its inception. However, the Go team has finally set the default value of GO111MODULE to on in version 1.16 of the Go language. This is a sign of the maturity of the go module mechanism. At the same time, it also shows that the Go team has started to popularize the go module mechanism.

From the standard tools provided by Go, all the old go commands have been fully adapted to the go module mechanism. For example, the go get command can now be used to adjust Go module dependencies, the go install command can now be used to download, compile, and install Go modules, the go test command can now be used to compile and test Go modules, and so on.

Profiles for Go modules: Module graph pruning

Let’s talk about the Go module configuration files. As you may already know, the implementation of the go module mechanism revolves around the configuration files go.mod and go.sum. These two configuration files are automatically generated by the go mod command in the root directory of the current Go module (or main module).

The first change is that the direct dependency module record and the indirect dependency module record for the main module have been made complete in the go.mod file. (Directly dependent modules and indirectly dependent modules will be referred to collectively as dependency modules in the following)

Prior to version 1.17, the Go team had been tweaking the way dependency modules were documented appropriately. For example, in version 1.16, the go.mod file did not record indirect dependency modules. In this case, some go commands (such as go build) would need to go through and find all the modules that the main module might depend on. Even if a module is only tied to a real dependent module, but is not actually dependent on the main module, it will still be recognized as a dependent module by those go commands. But in fact, the dependency should be in quotes here.

Therefore, if the “dependent” module is not yet stored on the local computer, the go command will report an error. It will report “Missing such-and-such ‘dependency’ module”, i.e., the “dependency” module is not found on the local machine. This obviously doesn’t make sense. Since it is not actually used, it should be ignored. Why bother looking for it? Moreover, you shouldn’t report an error if you can’t find it.

In fact, it is unlikely that this will happen. But since there was a lot of skepticism in the Go community about this issue, the Go team finally overhauled and fixed it in version 1.17.

If the main module’s go.mod file declares a Go version of 1.17 or higher, then the go mod command will record all direct and indirect dependencies of the main module in that file. This way, other go commands can look for only those modules that are documented here. Clearly, the go mod command has a much more logical division of labor with the other go commands. This behavior in the new version is also known as module graph pruning ( https://go.dev/ref/mod ).

Although module graph pruning may cause the main module’s go.mod file to record much more than before, it is generally not a big problem. Moreover, the new version of the go.mod file has been significantly optimized in terms of readability.

In addition, the go mod tidy command adds support for the -go flag in order to make updating the go.mod file smoother. We can append this flag when executing the command, and use it to modify the declaration of the Go version used in the go.mod file (as reflected in the go command), e.g.

1
go mod tidy -go=1.17

When such a command is executed, not only will the go directives in the go.mod file be changed, but the corresponding dependent module records will also be modified with the version specified here (as we said earlier).

Go module’s configuration file: new directives

In version 1.16, the Go team added a new directive to the go.mod file. The name of this directive is retract, which we can think of here as “retract”.

The retract directive is used when we have already released a version of the current module to the public, but for some reason want to retract it.

What this directive does is tell the go command that “a version of the current module should not be used by others”. We can tell an old version to be “retracted” as new versions are released to the public. For example, we want to commit version 1.2.3 of module A and tell the public that “version 1.2.2 of this module has a high-risk vulnerability” and that it is highly recommended that people do not use it anymore. Then, we can add to module A’s (version 1.2.3) go.mod file.

1
2
// 此版本存在一个高危漏洞,请尽快升级!
retract v1.2.2

After the 1.2.3 version of this module has been successfully pushed to the repository, the user will see a warning on the display when trying to update the module’s information with some go command (e.g. go list -m -u).

Configuration files for Go modules: new comments

The retract directive we just mentioned is used to “retract” a version of a module. The deprecation comment added to the go.mod file in version 1.17 of the Go language is used to deprecate an entire module.

When we want to upgrade the current module to a non-backward-compatible version (e.g., from v1 to v2), or when we want to drop maintenance of the current module altogether, we can consider adding deprecation comments to the module directive in the go.mod file, such as

1
2
3

// Deprecated: use example.com/mod/v2 instead.
module example.com/mod

The implication of the deprecation note here is that the mod module has been updated to a non-backward-compatible major version (v2) and that the old major version (v1) will not be further maintained. This is equivalent to advising and urging all developers who are using the v1 version to migrate to the v2 version.

To explain, the prefix “// Deprecated:” for deprecation comments is fixed. It should be followed by the corresponding deprecated note, and that note cannot be written on a new line. However, the entire deprecated comment can be written to the right of the module directive, in addition to being immediately above it, e.g.

1
module example.com/mod // Deprecated: use example.com/mod/v2 instead.

Note that changes to the go.mod file need to be committed to the repository along with the v1 update of the current module. This is so that the comment will work and the user will see a prompt when trying to update the module information.

Standard commands

Regarding the Go language’s built-in standard commands, in addition to the tweaks we’ve already mentioned with the module management optimizations, the Go team has made further enhancements to them in terms of ease of use.

go install command

First, in version 1.16, Go officially improved the go install command to accept a version suffix (e.g., @v1.0.0) as a way to download, compile, and install (collectively, install) a particular version of a package of code. Where “@” must be the character at the beginning of the version suffix. The version number after it follows the semantic version specification ( https://www.infoq.cn/article/CEoMOxgW4X7GCYr4eUEi ) (with some exceptions, see the version lookup documentation at https://go.dev/ref/mod ). The version number of the Go language itself actually follows this specification. In this case, we can use the go install command as follows.

1
go install example.com/cmd/pkg@v1.0.0

The above command installs version 1.0.0 of the package example.com/cmd/pkg. If the package is executable (i.e., the main package), then the command will place the resulting executable in the directory pointed to by the system environment variable GOBIN (provided that this system environment variable is set). Otherwise, the command will place the generated archive (a file with the .a extension) into a dedicated build cache directory. In either case, however, the go install command will first place the relevant files it downloads into the module cache directory.

As you may already know, under the go module mechanism, the go install command usually looks for the go.mod file in the current directory or its parent directory first, and then installs the specified version based on the information recorded there for the corresponding package (if any). However, if we use the go install command with a version suffix, the command will ignore the go.mod file and install the version we explicitly specify.

go get command

Starting with version 1.16, Go officially recommends that developers use only the go install command to install packages under the go module mechanism. Although the go get command can also be used to install packages, it also modifies the corresponding go.mod file under the go module mechanism. This is why the author said earlier that “the go get command can be used to adjust the dependencies of Go modules”. Obviously, the two commands currently overlap in terms of functionality. This is likely to cause problems for developers.

Therefore, Go officials strongly advise developers to carry the -d flag when using the go get command. This flag causes the go get command to modify only the corresponding go.mod file (i.e., adjust the module’s dependencies), without downloading, compiling, and installing it.

In fact, the go get command without the -d flag was deprecated in version 1.17 of the Go language. Also, in the upcoming 1.18 release, the go get command will have the -d flag enabled by default. That is, starting with 1.18, the go get command will behave as if it carried the -d flag even if we use it without the -d flag. This way, the go get command and the go install command can each do their own thing. Accordingly, commands like go build and go test will no longer modify any configuration files in the Go module.

By the way, when we use the go get command to adjust the dependencies of the current module, we can use the @none version suffix if we want to remove a direct dependency from the go.mod file.

In addition, version 1.16 of Go deprecates support for the -insecure flag in the go get command. This flag has been removed in Go 1.17. This flag was originally used to get Go code packages from insecure sites (such as sites based on the HTTP rather than HTTPS protocol), and to bypass security checks on modules. If we still need to use these features, we can do so by setting the system environment variable GOINSECURE, GOPRIVATE or GONOSUMDB.

Standard Library

Here, we’ll briefly talk about the changes in the Go language standard library. There are many changes, both big and small, but none of them are critical in the overall scheme of things. Therefore, the authors will only mention those new or deprecated packages here.

3 new code packages added

Starting with version 1.16, the runtime/metrics package was added to the Go language’s standard library. In short, this code package was introduced to make it easier for Go programs to get its various metrics on their own while they are running. There are many such metrics, related to garbage collection, memory usage, concurrent scheduling, etc. ReadMemStats function and the debug.GCStats structure, and is more general and efficient.

In addition, the io/fs package and the embed package were both introduced in version 1.16.

The io/fs package represents a new file system model, or rather a unified high-level abstraction of the file system. It has resulted in changes to a number of packages in the Go language standard library and new APIs, all for the benefit of the Go language and developers.

The embed package is used to embed additional resources in the Go executable, such as text files, image files, audio and video files, and other data files. Moreover, we can specify multiple files and even directories for this purpose. This involves the judicious use of the comment directive //go:embed.

I’ve already briefly explained the io/fs and embed packages in a previous article in this series (i.e., Interpreting Go 2020: The Night Before the Change https://www.infoq.cn/article/CEoMOxgW4X7GCYr4eUEi ), so I won’t go over them again here. For developers who want to get the most out of these new packages, the authors strongly recommend carefully reviewing the documentation for the Go standard library and, if necessary, reading the relevant Go language source code.

Deprecated io/ioutil packages

The Go team has now decided that the io/ioutil package is a poorly defined and incomprehensible collection of programs. Therefore, starting with version 1.16 of the Go language, all of the functionality provided in this package (the main implementation code) has been migrated to other packages (such as the io and os packages). However, to maintain backward compatibility, the io/ioutil package will be retained and will provide the correct functionality as before. The following is a list of features migrated for this purpose.

  • The functionality of io/ioutil.Discard has been moved to io.Discard.
  • The functionality of io/ioutil.NopCloser has been moved to io.NopCloser.
  • ReadAll has been moved to io.
  • the functionality of io/ioutil.ReadDir has been moved to os.ReadDir (note, however, that the type of the first result value returned is different, the former being []fs.FileInfo and the latter being []os.DirEntry)
  • The functionality of io/ioutil.ReadFile has been moved to os.ReadFile.
  • the functionality of io/ioutil.TempDir has been moved to os.MkdirTemp; * the functionality of io/ioutil.TempDir has been moved to os.
  • The function of io/ioutil.TempFile has been moved to os.CreateTemp; * The function of io/ioutil.
  • The function of io/ioutil.WriteFile has been moved to os.

Grammar

Version 1.17 of the Go language adds a small but powerful improvement: support for conversions from slices to array pointers. More specifically, slices of type []T can now be properly converted to array pointers of type *[N]T, e.g.

1
2
3

slice1 := []int{0, 1, 2, 3, 4}
array1 := (*[5]int)(slice1)

However, it is important to note that the second line of code here will immediately throw a runtime exception (i.e. panic) if we are given an array pointer of an improper type. For example, if the value (representing the length of the array) in our given type is greater than the actual length of the slice to be converted, as in the code (*[6]int)(slice1), then the program will crash due to the exception thrown here (if not handled properly).

About Performance

As a rule, the Go team improves some aspect of the Go language’s performance every year. This year is certainly no exception.

In version 1.16, the Go language’s linker has received a further performance boost. On a 64-bit Linux operating system, linking is 20-25% faster than in 1.15, and linking operations take up 5%-15% less memory space. On other computing platforms, such performance improvements are even greater. In addition, Go programs typically produce smaller binaries after processing, thanks to more aggressive symbol pruning. By the way, the computing platform in question is a combination and umbrella term for computing architectures (e.g. 386, amd64, arm, etc.) and operating systems (e.g. windows, linux, darwin, etc.). darwin is the Go language codename for the macOS operating system.

In version 1.17, the Go team implemented a new way to pass function argument values and result values using registers instead of the stack. As you know, the stack refers to a block of space in memory. So, using the stack usually allows you to not care about the differences between individual computing architectures (and different CPU models). However, its disadvantage is also obvious, namely, poor performance. As you may know, registers are memory devices in the CPU. Therefore, using registers forces you to pay attention to something more fundamental like the compute architecture. This is obviously the more difficult but better way. All in all, this new approach improves the performance of Go programs by about 5%. Also, Go programs typically produce binaries that are about 2% smaller. The Go language is now automatically enabled on 64-bit computing architectures on Linux, macOS, and Windows operating systems.

Other Updates

As you know, Apple has already introduced its own ARM computing architecture CPU and used it in its own computers. As a result, a new computing architecture-OS combination (i.e. computing platform) has emerged: darwin/arm64.

Previously, in the Go language, the iOS operating system was codenamed darwin/arm64. At that time, Apple only used ARM computing architecture CPUs for its smartphone iPhone and tablet iPad. However, today is different, so the Go team has adapted the new combination in version 1.16 of the Go language.

They have identified the computing platform designation for the macOS operating system as darwin/arm64, while the original iOS operating system designation has been renamed ios/arm64. This means that there is a new OS codename in the context of the Go language: ios. Together with darwin, it now covers the major operating systems released by Apple.

In addition, Go 1.16 adds a computing platform codenamed ios/amd64. This is another new combination. This computing platform targets iOS emulators running on top of the macOS operating system, which is based on the 64-bit AMD computing architecture.

Later, in version 1.17, the Go language supported a new combination of Windows operating systems and 64-bit ARM computing architectures, codenamed windows/arm64. This also reflects the convergence of the mobile computing platform with the former desktop computing platform.

Well, the above is a brief overview of the main changes in the Go language in 2021. In the author’s opinion, the most exciting changes for developers in the year are the significant improvements to the module management features and related commands. Of course, the three new packages in the Go standard library are also important.

Future Outlook

In recent years, the number of Go language users in China has been growing dramatically. Along with this growth has come a lot of dissatisfaction with the Go language. We often joke that “a programming language that isn’t already complaining is definitely not a popular language”. It’s a joke, but there’s some truth to it. Once people use it more, they will definitely find one or another problems in a programming language. No one is perfect, and this is especially true for programming languages.

For Go, there are three main issues that Chinese developers complain about, namely: module management tools, generic syntax support (hereinafter referred to as Go generics), and the way program errors are handled.

The Go team has tried to address the handling of program errors, but has yet to find a solution that satisfies them. Moreover, according to the author’s estimation, the earliest the Go team will bring Go generics back to the table is when it is complete and stable enough. Therefore, it will not be discussed in this article.

So, let’s take a look at the future of the Go language, starting with the first two issues.

Module Management Tools

As mentioned earlier, the Go team has actually pretty much solved the module management tools. However, in 2022, there will be some improvements to the Go language in this area.

For example, in the 1.18 release next February, the Go language will support a new mode called workspace mode. If you’ve been using Go, you may remember the workspace directory under the GOPATH mechanism. But the workspace mode here is a completely different thing from the workspace directory. So please be careful and don’t get confused.

By default, the standard commands under the go module mechanism will look for dependencies of the current module on the network. They will look at both public repositories supported by the Go language (such as Github) and private repositories that we have built ourselves (provided we have set them up accordingly).

However, when we maintain multiple Go modules with dependencies on our local machine, we often want to make changes in the dependent module instantly available to the modules that depend on it (the main module), without pushing the changes to the repository.

With the current go module mechanism, we can satisfy this need by adding the replace directive to the go.mod file of the main module. The replace directive creates a mapping between the import path of a dependent module and its storage path on the local machine, like this.

1
replace example.com/mod => /Users/haolin/GoWorkspace/mod

The above command creates a map for a module with an import path of example.com/mod, pointing its import path to its storage path on the local computer (with the macOS operating system). This way, the go command will be able to find the dependent module named mod without any problems when compiling the main module. Then, when we run the main module, it will promptly reflect the new changes in the dependent module (on the local computer).

This solution obviously works. But it introduces a problem: it mixes private configuration for the local development environment with public configuration for the whole project (i.e. it’s all placed in the go.mod file). This forced us to carry the private configuration with us when we submitted the public configuration to the code repository. This private configuration does not do any good for other developers to understand, use and improve the current module, and it pollutes the public configuration. More importantly, it may also make it impossible for other developers to compile the module successfully. This is because the path to the dependent module store in its private configuration is likely to be incorrect on other computers.

This is the main reason why the go module mechanism’s workspace pattern was created. At the time of writing (i.e., early December 2021), this feature is still under development and some of the things are not yet fully defined.

However, the pattern is expected to involve a new standard command go work, a new configuration file go.work, and a new configuration command directory. In short, the go work command can be used to create a workspace directory with several Go modules. The go work command also generates a go.work file in such a directory and adds the corresponding directory directive to it. This go.work is the file that holds the local development environment configuration. It can also have the replace directive in it. This way, we can exclude the go.work file when committing and pushing code, and thus avoid uploading private configuration.

Generic syntax support

Generics are also often referred to as type parameters. What we mean here by generic syntax support is actually support for developer-defined generic types and generic functions.

For the record, the authors do not want to go into the definition and design of generics here. Because that is a very large topic, and I am afraid that even a full article may not be enough to cover it. The author just wants to briefly explain his thoughts on Go generics in two lines.

The first line, the programming style line.

The Go language has actually had some support for generics for a long time. Types like arrays, slices, and maps that are built-in to the language and can be used as data containers (hereafter referred to as container types) actually support generics from the beginning, such as []Int and map[int]string. However, the standard library-level container types that were added later do not have generic support, such as the types List and Ring in the container package. This is because there is no syntax for custom generics in Go.

Go is a minimalist, engineering-oriented, general-purpose programming language. This is deeply reflected in the language syntax, standard libraries, and standard tools. When a concept, a syntax, or a feature is formally introduced, it will not overlap or be confused with the rest of the Go language. That is, they are all orthogonal to each other.

Go programs can have interface declarations in them. We can say that interfaces take on the role of type abstraction and type constraints in Go programs. However, the arrival of custom generics will change this division of responsibilities.

By design, custom generics in the Go language will join with interfaces to form new orthogonal combinations that will bring out much more powerful type abstraction than before. It is no exaggeration to say that this will give the Go language a dimensional boost in this area. It is similar to moving from a photo based on a two-dimensional presentation to a holographic projection based on a three-dimensional presentation.

However, while we should rejoice, we should also be prepared. This dramatic shift will inevitably lead to a steep increase in program complexity and new challenges for the Go language and Go program simplicity. While the Go team has gone to great lengths to simplify the syntax of custom generics and minimize the associated modifications that come with them, as evidenced by the several version updates to the Go generic design scheme, the significant impact on the Go language and its programming style is inevitable. For the average developer, the least we can do is get used to the square brackets “[” and “]” in the generic definition, the vertical line symbol “|” and the wavy symbol “~” in the new interface definition, and the symbols “~”, and so on. This obviously puts more of a mental burden on us, at least for developers who have not been exposed to generics.

Finally, generics, like other syntaxes that have great appeal, are sure to tempt developers to use them everywhere, even abuse them. Such abuse is likely to mean a deep hole for the developers who maintain the program afterwards. This is the main reason why there is very little syntactic sugar in the Go language.

Second line, the backwards compatibility line.

Like the module management tools, the Go language’s generic syntax support is a well-worn issue. Developers have been clamoring for it for years, probably for years. Even if it’s only been 4 years since “the Go team agreed to add generics to the Go language”.

The Go team released the Go2 draft with generic syntax support in late 2018. After that, there were several tweaks and refinements until August 2021, when the Go team released a final design: the Type Parameters Proposal (https://github.com/golang/proposal/blob/master/design/43651-type-parameters.md). At this point, a generic model that closely matches the Go language was officially released.

In fact, the 1.17 release of Go already contains some code related to custom generics. But they are not available to the public. As you can see, the Go team was very careful about this major improvement. They are taking one small step at a time.

Rob Pike, the founder of the Go language, said that “adding Go generics is the single biggest change since the official release of the Go language”. As a result, even Go 1.18 will only include a syntax that supports custom generics, and will barely include changes to custom generics in its standard library. Except for a code package called constraints, which is the basis for writing custom generics. Some relevant changes will be made first in the experimental module golang.org/x/exp. This experimental module does not guarantee backwards compatibility. This is an absolute guarantee of backwards compatibility for the official release of the Go language.

If nothing else, we can write code with generics using version 1.18 of the Go language. However, we will have to wait until Go 1.19, or even Go 1.20, if we want to use Go generics without any problems. Only then will Go’s official generic programming best practices be available, and the various generic-related packages in the standard library will be ready. And, since the changes to generics are relatively large, it will be hard for those popular third-party tools to catch up anytime soon. For now, it’s all about taking your time.

The Go team believes that maintaining stability and backward compatibility is the most important thing. The authors couldn’t agree more. What’s more, the Go team’s Go generic design solution is quite good, and only equally good implementation code can match it. So, don’t be in a hurry on this one. Good things come in small packages. We’ve waited so long anyway, we’re not afraid to wait a little longer.

In short, the authors strongly recommend that you actively experience Go generics, and those experimental generic packages, in the next year. But at the same time, the author also recommends that you don’t take too big a step to avoid getting hurt.

Other expectations

In addition to the above-mentioned updates related to module management and Go generics, the Go team will be making a number of improvements in version 1.18 of the Go language. Here is a brief list of some of the more notable changes.

  • Fuzzing: This will be a new form of program testing supported by the Go language, also known as fuzzy testing. This test automatically generates input data (usually random) that matches the program’s requirements, and makes continuous calls to the program. The purpose of this is to test whether that program reacts as expected to various input data (including non-normal data). Such random data will certainly be much more comprehensive than the input data provided manually by the developer. Therefore, fuzzy testing usually allows for a more thorough examination of the program. To support fuzzy testing, the go test command accepts the new token -fuzz, a new test function naming format FuzzXxx is added to the test file, and a new type F and a set of new methods are added to the testing package.
  • Information embedded in files: The new go command will embed version control information into the binary file, including the current checked-out revision, commit time, and a flag indicating the status of the current file (e.g., “edited”, “untracked”, etc.). " etc.). If the Go project is using one of the major version control systems (Git, Mercurial, Bazaar, etc.), this is turned on by default. To ignore this message, we can append the flag -buildvcs=false to the go command. In addition, the embedded messages will contain some build-related flags (which can be ignored with the flag -buildinfo=false). To read this information, we can either run the go version -m <file> command, or call the runtime/debug.ReadBuildInfo function, or use the new debug/buildinfo package. The author remembers that we used to manually add the above information to the source code of Go programs for ease of deployment and maintenance, and provide external markers to print them. Well, now this work can finally be automated.
  • A new method for locks: the TryLock method. The type sync.Mutex will own this method. If the current goroutine already holds the lock when we call this method, then the method will immediately return the result value true, without blocking the current goroutine. In addition, the sync.RWMutex type will also have a TryRLock method to try to lock the read lock within it. This method implements a feature that developers (including authors) have been waiting for. Until now, it was difficult to find out if the current goroutine already held a lock (repeated calls to the Lock method of that lock would cause the current goroutine to block).

The above updates are what the authors are most looking forward to and represent their personal opinions only. The authors believe that different developers will have different expectations for the new version of the Go language. A more comprehensive and detailed description of the Go 1.18 update can be found at ( https://tip.golang.org/doc/go1.18 ) (if Go 1.18 is officially released by the time you read this, try visiting ( https://go.dev/doc/go1.18 ))

Also, of course, the author is looking forward to a larger presence of the Go language in China and a larger and more diverse technical community there. In fact, such a trend already exists. But the author still hopes to add to this trend.

Summary

Okay, let’s quickly recap.

If there’s one word to represent the Go language in 2021, it’s “stable”.

As Go fans know, the Go team has been building up its strength in recent years. Finally, this year, Go’s module management features have been significantly improved, and a new file system model has been added to the standard library. The problem of packaging resource files, which used to cause a lot of inconvenience, was solved with the addition of the embed code package.

Like other enthusiasts, the authors have some concerns while looking forward to Go generics. We were worried that Go generics would break the backwards compatibility of the Go language, that the syntax of Go generics would be too complex, and that its arrival would make Go programs significantly more difficult to develop and read. Fortunately, thanks to the Go team’s preparation and design expertise, all of these fears have disappeared. Moreover, the authors are confident that the Go team will follow up with enough useful best practices to help us use Go generics more correctly and efficiently.

If the key word for the Go language update in 2021 is “module management”, then the key word for its update in 2022 must be “(custom) generics”. However, both these larger updates and the relatively small improvements not mentioned in this article are making Go a better general-purpose programming language.

To developers of Go programs, I say “we should have enough faith in the Go language and the Go team to believe that it and they will both be stable”. On the other hand, if you haven’t used the Go language, then the authors would strongly recommend that you give it a try. Download the Go language from ( https://go.dev/dl/ ) and install it on your computer right now!