go1.13

1. Numeric literals have been changed

Prior to version 1.13, Golang only supported decimal and hexadecimal literals, while other languages, which use widespread binary and octal numbers, did not. For example, the following code would not compile:

fmt.Println(0b101) 
fmt.Println(0o10)
Copy the code

In go1.13, the above literal syntax is already supported. You can indicate a binary literal with an 0b or 0b prefix, and octal literal with 0o and 0o. It is worth noting that gofmt is converted to all lowercase by default, although it can be written either way

1.13 The following error message is displayed if the following version is run:

# command-line-arguments
usercode/file.go:6: syntax error: unexpected name, expecting )
usercode/file.go:7: syntax error: unexpected name, expecting )    
Copy the code

Prior to version 1.13, there was no support for 0b or 0b to indicate a binary literal, or even 0o and 0O to indicate an octal literal

One final small improvement to numeric literals is that you can now separate numbers with underscores for readability.

fmt.Println(100000000)
fmt.Println(1_0000_0000)
fmt.Println(0xff_ff_ff)
Copy the code

1.13 Returned Result:

100000000
100000000
16777215
Copy the code

The number of characters between the delimiters is not specified to be equal, but for readability it is best to separate the delimiters every three or four digits as is customary

1.13 The following error message is displayed if the following version is run:

# command-line-arguments
./code.go:9: syntax error: unexpected _0000_0000, expecting comma or )
./code.go:10: syntax error: unexpected _ff_ff, expecting comma or )
Copy the code

Prior to version 1.13, splitting numbers by underlining was not supported to increase readability

First of all, Golang has zero tolerance for out-of-bounds references to arrays and slices, which can panic if they are out of bounds, as in the following example

package main import "fmt" func main() { arr := [...] Int {1,2,3,4,5} for I := 0; i <= len(arr); i++ { fmt.Println(arr[i]) } }Copy the code

1.13 The following error message is displayed if the following version is run:

1
2
3
4
5
panic: runtime error: index out of range
Copy the code

Version 1.13 before panic will not crossing the line of the output value, while the call stack trace information is not very difficult, to be convenient location problem, but if the invocation chain deep or you are in a high concurrency, things get trouble, either we log debug and finally analysis to rule out a lot of noise to locate the problem, Either rely on a breakpoint to single-step mode, no matter which requires a lot of energy and core question is why do we think of cross-border, shallow step again, we may just know that sometimes cause cross values can roughly determine the cause of the problem, it is a pity that the information provided by the panic of the does not contain the above content, until golang1.13. Golang now prints out the value that caused the transgression, no doubt sending carbon in the snow:

1
2
3
4
5
panic: runtime error: index out of range [5] with length 5

goroutine 1 [running]:
main.main()
        /Users/chengaosheng/go/test/test.go:46 +0xec
exit status 2
Copy the code

Of course, no matter how complete panic information is, it is not a panacea. Perfect unit testing and rigorous work attitude are the best protection against bugs.

3. Tool chain improvement

Aside from the removal of the Godoc program, the biggest changes still focus on the Go Modules.

3.1 GOPROXY

Proxy.golang.org,direct. This is a comma-separated list with the same values for the following two variables, where direct means direct without proxy. If set to off, Download any package in base.

When the go get command obtains the package, it will search from left to right. If no matching package is found, an error will be reported.

Needless to say, proxy gives domestic developers unimpeded access to packages that are not available in the domestic environment. More importantly, the default proxy is officially provided and maintained, which provides greater security than third-party solutions.

3.2 GOSUMDB

This variable essentially specifies an officially managed online go.sum database. Golang validates packages

The package downloaded by Go get will create a hash string based on the go.mod file and all downloaded files respectively and store it in the go.sum file. The downloaded package will be cached, and every time you compile or manually go mod Verify, you will recalculate the comparison with the value in Go. sum, and a security error will be reported if any inconsistency occurs. This mechanism is based on the local cache above will not change throughout the development life cycle (because of dependent libraries rarely updated versions, unless there is a major security issues), the mechanism can avoid others mistakenly update dependence or local malicious tampering, but now more security problem is happened in the remote environment, So this mechanism has a big security risk.

Fortunately, GOSUMDB has been added, its default value is “sum.golang.org”, some parts of China are not accessible, can be changed to “sum.golang.google.cn”. Here’s how it works now:

Go get Downloads the package and calculates the checksum. After the calculation is complete, it will check whether the file has appeared in go.sum first. If not, it will check in GOSUMDB. If the checksum of the corresponding version of the package is already in go.sum, GOSUMDB is not requested, and the remaining steps are the same as in the old mechanism. Security has been enhanced.

3.3 GOPRIVATE

Finally, GOPRIVATE, which defaults to empty, allows you to use Linux Glob wildcard syntax to specify that certain packages or classes of packages are not downloaded from the proxy, such as packages automatically generated by some RPC suites, which do not exist in the proxy and would not make sense if uploaded. So you need to write it into GOPRIVATE.

A similar environment variable called GONOPROXY has the same value and basically the same function, but it overrides GOPRIVATE. For example, if it is set to None all packages are fetched from proxy.

4. New features of the standard library

Every new release brings a host of new features to the standard library, and this one is no exception.

4.1 Determine whether the variable is 0

Any type of 0 in Golang is clearly defined. Unfortunately, different types of 0 have different values, especially custom types, and if you want to determine whether a variable has a zero value, you can write complex and difficult to extend code.

So Reflect adds this feature to simplify things:

package main

import (
        "fmt"
        "reflect"
)
 
func main() {
        a := 0
        b := 1
        c := ""
        d := "a"
        fmt.Println(reflect.ValueOf(a).IsZero()) // true
        fmt.Println(reflect.ValueOf(b).IsZero()) // false
        fmt.Println(reflect.ValueOf(c).IsZero()) // true
        fmt.Println(reflect.ValueOf(d).IsZero()) // false
}
Copy the code

Of course, the cost of reflection once and for all is higher performance consumption, so the specific choice should be based on the actual environment.

4.2 Error handling innovations

It’s not really an innovation, just a tinkering with existing practices. The Golang team has always felt that error is a value and therefore must represent the equal operation of value, so the whole thing is strange.

First, we introduce error Chains.

In 1.13, we can implement an Unwrap method for error, for example:

type PermError { os.SyscallError Pid uint Uid uint } func (err *PermError) String() string { return fmt.Sprintf("permission error:\npid:%v\nuid:\ninfo:%v", err.Pid, err.Uid, Err.SyscallError)} func (err *PermError) Error() string {return err.string ()} func (err *PermError) Unwrap()  error { return err.SyscallError }Copy the code

Suppose we wrap a permission error based on SyscallError, including all errors raised because of permission problems. The String and Error methods are both implemented in normal custom errors, but we’ll focus on the Unwrap method.

Unwrap literally means to Unwrap, which means we Unwrap the previous layer of errors and return it. Os.syscallerror also implements Unwrap, so you can go back up to the original error that did not implement Unwrap. We call an error chain from PermError to the top level.

If we use → to refer to the object returned by Unwrap, the following structure will be formed:

PermError → os.SyscallError → error

More complex structures can also occur: A → Err1 ___________

|

V

B → Err2 → Err3 → error

If you don’t want to define an error type by yourself and just want to add some information, you can rely on FMT.Errorf:

newErr := fmt.Errorf("permission error:\npid:%v\nuid:\ninfo:%w", pid, uid, sysErr)
sysErr == newErr.(interface {Unwrap() error}).Unwrap()
Copy the code

Errorf The new placeholder %w can only appear once in a formatted string, it will fill in the error information, and then return a new error implementing Unwrap, which returns the error passed in. In addition, the Wrapper interface in the proposal is not currently implemented, but the standard library temporarily implements the Wrapper functionality using what I have done above.

Because of the error chain, we can no longer simply use equal signs to judge value based errors, but the advantage is that we can now judge type based errors as well.

In order to continue to allow error to represent its value semantics, the Errors package adds Is and As and the Unwrap functions that support them.

Unwrap calls the Unwrap method of the passed argument, which Is used by As and Is to trace the entire chain of errors.

Code like the previous section can be simplified like this:

newErr := fmt.Errorf("permission error:\npid:%v\nuid:\ninfo:%w", pid, uid, sysErr)
sysErr == errors.Unwrap(newErr).Unwrap()
Copy the code

Sometimes an error Is just a wrapper around another error. When this error Is generated, another error has already occurred. In this case, we only need to compare the upper error value, and then you need errors.Is to help:

newErr := fmt.Errorf("permission error:\npid:%v\nuid:\ninfo:%w", pid, uid, sysErr)
errors.Is(newErr, sysErr)
errors.Is(newErr, os.ErrExists)
Copy the code

You never know how the program will be extended or how the relationship between errors will change in the future, so it’s safe to always use Is instead of ==.

However, there are always exceptions. For example, IO.EOF does not need to be compared with Is because it Is not an error in the procedural sense, and it Is generally not wrapped.

As In addition to traditional value-based judgment, handling a certain type of error is a common requirement. For example, both A and B in the previous section come from error. Assuming that we now want to handle all errors based on this error, the common way is for the switch to compare or rely on the polymorphism ability of the base class.

It is obvious that switch’s approach to judgment results in a lot of repetitive code and is difficult to extend; In Golang, there is no inheritance, only composition, so the only interface with runtime polymorphism is interface. In this case, we can only rely on errors.As to help:

// Note that the second argument to As can only be a pointer to the type you want to check

var p1 *os.SyscallError
var p2 *os.PathError
errors.As(newErr, &p1)
errors.As(newErr, &p2)
Copy the code

If p1 and p2 are on the same error chain as newErr, this returns true, implementing a crude polymorphism effect. As is always used instead of if _, ok := err.(type); Ok code like this.

Of course, the above functions save you a lot of code, but also rely heavily on reflection, especially if the error chain is long and needs to be retraced multiple times, so here are two pieces of advice:

Do not over-packaging, nothing can not be solved by adding an indirect layer, but too much middle layer will not only affect performance but also interfere with subsequent maintenance; If you really care about performance and there are no extensions to existing errors (such as io.eof), then it doesn’t hurt to use a traditional solution.

go1.14

1. The tool

1. Commands such as go build use -mod=vendor by default. If you need to use mod cache, you need to display -mod=mod. 2. Go mod init sets go.mod file to -mod=readonly, and go.mod is read-only. 3. The go command other than go mod Tidy no longer edits the go.mod file 4. Go Get will no longer upgrade to an incompatible major version of the module unless specifically requested or already requested. Go List also ignores incompatible version 5 of this module when retrieved directly from version control. In Module mode, go command supports SVN repository 6. Go test -v now streams t.log output instead of output at the end of all test dataCopy the code

2. Goroutine supports asynchronous preemption

In THE Go1.1 version, the scheduler does not support preemptive scheduling and can only rely on Goroutine to actively surrender CPU resources, which has a very serious scheduling problem.

In Go1.12, the compiler inserts a function at a specific time and triggers preemption through function call as an entry, thus realizing cooperative preemption scheduling. But there are some edge cases with this scheduling approach that requires active coordination of function calls, such as the following example:

func main() {
    runtime.GOMAXPROCS(1)  
    go func() {
            for {
            }
    }()
    time.Sleep(time.Millisecond)
    println("OK")
}
Copy the code

In the above code, where a goroutine is created and suspended, the main Goroutine calls sleep first, and the only P goes to the goroutine created by executing the for loop, and the main Goroutine is never scheduled again. In other words, before Go1.14, this code would never have printed OK, because cooperative preemptive scheduling would not have preempted a Goroutine that did not voluntarily relinquish execution and did not participate in any function calls. Go1.14 solves the above problems by implementing true preemptive scheduling based on signals. When Go1.14 is started, the SIGURG signal handler Runtime.dosigpreempt is registered in runtime.sighandler, and when the stack scan for garbage collection is triggered, the function is called to suspend goroutine and send a signal to M. Upon receiving the signal, M will put the current Goroutine to sleep and continue to execute other Goroutines.

3. Add CleanUp method to testing package

The T, B, and TB of the testing package are all added with the CleanUp method, which executes F (if multiple registrations occur) on a lifo basis. Test cleanup, clear resource:

func TestCleanup(t *testing.T) {
   t.Cleanup(func() {
      t.Log("clear resource")
   })
   t.Log("test cleanup")
}
Copy the code

4. Allow embedding of interfaces with overlapping method sets

The following interface definitions were not allowed prior to Go1.14

type ReadWriteCloser interface {
    io.ReadCloser
    io.WriteCloser
}
Copy the code

IO.ReadCloser and IO.WriteCloser Close duplicate method Close This repetitive set of interfaces is supported in Go1.14

go1.15

1. Version 1.15 changes

1. Substantial improvements to the Go linker 2. Improved allocation of small objects with high core counts 3.X.509 CommonName deprecation 4.GOPROXY supports skipping proxies that return errors 5. New time/ TZData package 6. Some improvements to the core libraryCopy the code

2. Some core library improvements

2.1 Time. Ticker added a Reset method to support changing Ticker duration. Time. Ticker is a periodic timer with a built-in Channel that periodically transmits time. Use the time.newticker (d Duration) function to create a Ticker with a built-in channel field to which each time interval sends the current time. Tickers adjust the interval or discard messages to accommodate slow receivers.
func TestTickerReset(t *testing.T) {
	wait := make(chan struct{})
	ticker := time.NewTicker(time.Second * 1)
	go func() {
		defer close(wait)
		for i := 0; i < 5; i++ {
			t.Log(<-ticker.C)
			if i == 2 {
				ticker.Reset(time.Second * 2)
			}
		}
	}()
	<-wait
}
Copy the code
2.2 Time/tzData is a newly added package in Go 1.15. When the system cannot find time zone data, it can embed time zone data in the program by importing this package. Importing this package will increase the program size by about 800KB. Note that the time/tzdata package should be imported in the program’s main package, not in a libary project. The same effect can also be achieved by passing -tags timetzData at compile time.

3. The forms of panic display change

Before Go 1.15, If the value that passes to panic is bool, complex64, complex128, float32, float64, int, int8, int16, int32, int64, string, uint, uint8, uint16, Uint32, uint64, uintptr and other values of the native type. Then, panic outputs specific values when triggering, such as:

package main
func foo() {
    var i uint32 = 17
    panic(i)
}
func main() {
    foo()
}
Copy the code

1.15 And above:

panic: main.myint(27)

goroutine 1 [running]:
main.bar(...)
        /Users/chengaosheng/go/test/test.go:63
main.main()
        /Users/chengaosheng/go/test/test.go:56 +0x39
exit status 2
Copy the code

1.15 The following versions:

Panic: (main.myint) (0x105Fca0, 0xC00008E000) Goroutine 1 [running]: main.bar(...) / Users/tonybai/go/src/github.com/bigwhite/experiments/go1.15-examples/runtime/panic.go:12 main. The main () / Users/tonybai/go/src/github.com/bigwhite/experiments/go1.15-examples/runtime/panic.go:17 + 0 x39 exit status 2Copy the code

Go 1.15 optimized the presentation for this situation so that Panic can output values even for custom type variables derived from these native types.

4. The standard library

4.1 Adding restrictions on JSON decoding

Json package is one of the most commonly used GO standard library packages. In GO 1.15, GO adds a layer of restrictions to the DECODING of JSON according to the requirements of THE JSON specification. If the incoming JSON text data is indent beyond maxNestingDepth, the JSON package will panic. Of course, most of the time, you won’t be able to get very large JSON text with 10,000 indents. Therefore, 99.9999 percent of Gopher will not be affected by this limit.

4.2 reflect bag

Reflect pack prior to Go 1.15

Example:

package main import "reflect" type u struct{} func (u) M() { println("M") } type t struct { u u2 u } func call(v reflect.Value) { defer func() { if err := recover(); err ! = nil { println(err.(string)) } }() v.Method(0).Call(nil) } func main() { v := reflect.ValueOf(t{}) // v := t{} call(v) // v.M() call(v.Field(0)) // v.u.M() call(v.Field(1)) // v.u2.M() }Copy the code

1.15 And above:

172-15-70-45:test chengaosheng$ go run test.go 
M
reflect: reflect.Value.Call using value obtained using unexported field
reflect: reflect.Value.Call using value obtained using unexported field
Copy the code

We see that Reflect is unable to call the export methods of non-exported fields u and U2. However reflect can still use the u export method indirectly by promoting to the method of type T, as shown in the first line output from the run result. This change may affect legacy code that uses Reflect to call non-exported field methods in the form of type-embedded methods. If you have this problem in your code, you can simply promote to methods in the package type (t in the example) (call(v) in the example) instead.

go1.16

1. The binaries packaged by Go include co-compilation and packaging of configuration files

When static resource compilation cannot be packaged into a binary file, there are usually two solutions:

The first is to identify such static resources and whether they need to follow the program. The second is to consider packaging it into a binary file.Copy the code

In the second case, Go was not previously supported and people resorted to fancy open source libraries such as Go-bindata/Go-bindata.

But since Go1.16, the Go language itself has officially supported this feature

Demo code:

import _ "embed"

//go:embed hello.txt
var s string

func main() {
 print(s)
}
Copy the code

Embed Hello.txt //go: Embed Hello.txt //go: Embed Hello.txt The format of the annotations is simple: the Go: Embed instruction statement, plus the address of the content read, can support relative and absolute paths.

Output result:

Hello world
Copy the code

The contents of the static file are automatically assigned to the variable S and printed successfully in the main function.

For other base types, Go Embed is also supported:

//go:embed hello.txt
var s string

//go:embed hello.txt
var b []byte

//go:embed hello.txt
var f embed.FS

func main() {
 print(s)
 print(string(b))

 data, _ := f.ReadFile("hello.txt")
 print(string(data))
}
Copy the code

Output result:

Hello world
Hello world
Hello world
Copy the code

We made multiple embed annotation declarations simultaneously in a code file.

And for string, slice, byte, FS and other types of packaging, also do not need too much processing, very convenient.

2. How to Play guitar

In addition to basic usage, the Embed itself also supports multiple morphs on instructions:

//go:embed hello1.txt hello2.txt
var f embed.FS

func main() {
 data1, _ := f.ReadFile("hello1.txt")
 fmt.Println(string(data1))

 data2, _ := f.ReadFile("hello2.txt")
 fmt.Println(string(data2))
}
Copy the code

The go: Embed annotation can be read from multiple files at a time, and can also be read from multiple lines of a variable:

//go:embed hello1.txt 
//go:embed hello2.txt
var f embed.FS
Copy the code

You can also specify the directory helloWorld in the annotation and read the file accordingly:

//go:embed helloworld
var f embed.FS

func main() {
 data1, _ := f.ReadFile("helloworld/hello1.txt")
 fmt.Println(string(data1))

 data2, _ := f.ReadFile("helloworld/hello2.txt")
 fmt.Println(string(data2))
}
Copy the code

At the same time, since it can support directory reading, it can also support greedy mode matching:

//go:embed helloworld/*
var f embed.FS
Copy the code

Embed.FS can also tune all kinds of file system interfaces, the essence of which is that embed

3. Read-only attribute

In the FS provided by Embed, you can actually find both open and read-only methods:

type FS
func (f FS) Open(name string) (fs.File, error)
func (f FS) ReadDir(name string) ([]fs.DirEntry, error)
func (f FS) ReadFile(name string) ([]byte, error)
Copy the code

It is also clear from this that embed packages into the binary can only be read, not changed.

More abstractly, embed content is determined at compile time and cannot be modified at run time to ensure consistency.