SoFunction
Updated on 2025-03-04

Summary of small tips to improve Go language development efficiency (GO language syntax sugar)

Variable length parameters

GO allows a function to take any number of values ​​as parameters. GO has built-in **...Operators can only be used at the last formal parameter of the function...** operator, you must pay attention to the following things when using it:

  • The variable length parameter must be at the last of the function list;
  • When variable length parameters are parsed as slices, it isnilslice
  • The variable length parameter must be the same type
func test(a int, b ...int){
  return
}

Since our function can receive variable length parameters, we can also pass slices when passing parameters using **...** for unpacking and converting them into parameter list.appendThe method is the best example:

var sl []int
sl = append(sl, 1)
sl = append(sl, sl...)

The append method is defined as follows:

//	slice = append(slice, elem1, elem2)
//	slice = append(slice, anotherSlice...)
func append(slice []Type, elems ...Type) []Type

Declare an indefinite length array

Arrays have fixed lengths. When declaring an array, we must declare the length, because the array must confirm its length when compiling. However, sometimes for me who want to be lazy, I just don’t want to write the length of the array. Is there any way for him to calculate it by himself? Of course, when declaring an array using the **...** operator, you just fill in the element value and leave the others to the compiler itself;

a := [...]int{1, 3, 5} // The length of the array is 3, equivalent to a := [3]{1, 3, 5}

Sometimes we want to declare a large array, but someindexIf you want to set special values, you can also use the **...** operator:

a := [...]int{1: 20, 999: 10} // The length of the array is 100, the element value of subscript 1 is 20, the element value of subscript 999 is 10, and the other element values ​​are 0

initfunction

GO language provides precedentmainFunction executioninitFunction, will be automatically executed after initializing each package.initFunctions, each package can have multipleinitFunctions, there can also be multiple source files in each package.initThe loading order of functions is as follows:

Starting from the current package, if the current package contains multiple dependent packages, the dependent package is initialized first, and each package is initialized recursively. In each package, it is executed from front to back according to the dictionary order of the source file. In each source file, constants and variables are initialized first, and finally initializedinitFunction, when multipleinitWhen the function is executed in sequence from front to back. After each package is loaded, it returns recursively, and finally initializes the current package!

initThe function is implemented, no matter how many times the package is imported,initThe function will be executed only once, so useinitIt can be used in service registration, middleware initialization, singleton mode implementation, etc., such as the ones we often usepprofHe used the toolinitFunction, ininitRouting registration is performed in the function:

//go/1.15.7/libexec/src/cmd/trace/
func init() {
 ("/io", serveSVGProfile(pprofByGoroutine(computePprofIO)))
 ("/block", serveSVGProfile(pprofByGoroutine(computePprofBlock)))
 ("/syscall", serveSVGProfile(pprofByGoroutine(computePprofSyscall)))
 ("/sched", serveSVGProfile(pprofByGoroutine(computePprofSched)))

 ("/regionio", serveSVGProfile(pprofByRegion(computePprofIO)))
 ("/regionblock", serveSVGProfile(pprofByRegion(computePprofBlock)))
 ("/regionsyscall", serveSVGProfile(pprofByRegion(computePprofSyscall)))
 ("/regionsched", serveSVGProfile(pprofByRegion(computePprofSched)))
}

Ignore the guide packet

Go language has a clean code obsession with designers, so they should avoid code abuse as much as possible in design, so the GO language guide package must be used. If the guide package is not used, it will cause compilation errors. However, in some scenarios, we will encounter situations where we only want to guide packages but not use them, such as the one mentioned above.initFunction, we just want to initialize the packageinitFunction, but will not use any method in the package, you can use it_Operator symbol rename imports an unused package:

import _ "/asong"

Ignore fields

In our daily development, we usually pile up shit on shit, and we will reuse it directly when we encounter a method that can be used. However, we may not use all the return values ​​of this method. We also rack our brains to think of a name for them. Is there any way to not deal with the return values ​​you don’t need to deal with? Of course, there is, or_Operator, assign unnecessary values ​​to empty identifiers:

_, ok := test(a, b int)

json serialization ignores a field

We will be right for most business scenariosstructDo serialization operations, but sometimes we wantjsonSome fields inside do not participate in serialization. The **-** operator can help us handle it. The GO structure provides label function and is used in the structure label.-The operator can perform special processing on fields that do not require serialization, using the following:

type Person struct{
  name string `json:"-"`
  age string `json: "age"`
}

json serialization ignores empty value fields

We useSerialization will not be ignoredstructnull value in, default output field type zero value (stringThe zero value of type is "", and the zero value of object type isnil...), if we want to ignore these fields without values ​​in serialization, we can add them in the structure labelomitempty tag:

type User struct {
	Name  string   `json:"name"`
	Email string   `json:"email,omitempty"`
  Age int        `json: "age"`
}

func test() {
	u1 := User{
		Name: "asong",
	}
	b, err := (u1)
	if err != nil {
		(" failed, err:%v\n", err)
		return
	}
	("str:%s\n", b)
}

Running results:

str:{"name":"asong","Age":0}

AgeWe did not add fieldsomitemptyTag injsonThe serialization result is a null value.emailThe fields are ignored;

Short variable declaration

Every time I use a variable, I have to declare the function first. For a lazy person like me, I really don't want to write it because I write it.pythonIf you are used to writing, can you use it directly without variable declaration in GO? We can usename := expressionThe syntax form of declares and initializes local variables, compared to usingvarThe way of declaration can reduce the steps of declaration:

var a int = 10
etc.
a := 10

There are two comments when declaring short variables:

  • Short variable declarations can only be used within functions and cannot be used to initialize global variables.
  • Short variable declaration means introducing a new variable, and variables cannot be declared repeatedly in the same scope.
  • If one of the variables in a multivariate declaration is a new variable, then a short variable declaration can be used, otherwise the variable cannot be declared repeatedly;

Type Assertion

We usually useinterface, one is with a methodinterface, one is emptyinterfaceGo1.18There were no generics before, so we can use empty onesinterface{}To be used as a pseudogenetic when we use the empty oneinterface{}When used as an entry parameter or return value, type assertions will be used to obtain the types we need. The syntax format of type assertions in Go language is as follows:

value, ok := x.(T)
or
value := x.(T)

x isinterfaceType, T is a specific type, the first is a safe assertion, and the second is a failure of the assertion, panic will trigger; here type assertions need to be distinguishedxtype, ifxIt is an empty interface type:

The empty interface type assertion is essentially toefacemiddle_typeCompare with the type to match, successfully assemble the return value in memory, and directly clear the register if the match fails, return the default value.

ifxYes, non-empty interface types:

The essence of non-empty interface type assertion is in iface*itabComparison.*itabA successful match will assemble the return value in memory. If the matching fails, the register will be cleared and the default value will be returned.

Slice loop

Slices/arrays are operations we use frequently and are provided in the GO languagefor rangeSyntax to quickly iterate objects, arrays, slices, strings, maps, channels, etc. can all be traversed. In summary, there are three ways:

// Method 1: Only traverse and do not care about data, suitable for slices, arrays, strings, maps, channelsfor range T {}

// Method 2: traversal to get the index or array, slice, array and string are index, map is key, channel is datafor key := range T{}

// Method 3: traversal to obtain index and data, which is suitable for slices, arrays, and strings. The first parameter is the index, the second parameter is the corresponding element value, the first parameter is the key, and the second parameter is the corresponding value;for key, value := range T{}

Determine whether the map key exists

Go language provides syntaxvalue, ok := m[key]Let's judgemapIn-housekeyWhether it exists, if it exists, it will return the value corresponding to the key, and if it does not exist, it will return the empty value:

import "fmt"

func main() {
    dict := map[string]int{"asong": 1}
    if value, ok := dict["asong"]; ok {
        (value)
    } else {
      ("key:asong does not exist")
    }
}

Select control structure

GO language providesselectKeywords,selectCooperatechannelCanGoroutineWaiting for multiple simultaneouslychannelRead or write, inchannelBefore the state changes,selectIt will block the current thread orGoroutine. Let’s take a look at an example:

func fibonacci(ch chan int, done chan struct{}) {
 x, y := 0, 1
 for {
  select {
  case ch <- x:
   x, y = y, x+y
  case <-done:
   ("over")
   return
  }
 }
}
func main() {
 ch := make(chan int)
 done := make(chan struct{})
 go func() {
  for i := 0; i < 10; i++ {
   (<-ch)
  }
  done <- struct{}{}
 }()
 fibonacci(ch, done)
}

selectandswitchHas a similar control structure,switchThe difference is,selectIn-housecaseThe expression inchannelThe sending and receiving operation, whenselectTwo of themcaseWhen triggered at the same time, one of them will be executed randomly. Why is it executed randomly? Random introduction is to avoid the occurrence of hunger problems. If we execute them in sequence every time, if twocaseThe conditions have always been met, then the next onecaseIt will never be executed.

In the above exampleselectThe usage is blocking transmission and reception operation until there is achannelA state change occurred. We can alsoselectUsed indefaultStatement, thenselectThe statement will encounter these two situations when executing:

  • When there is aChannelWhen processing theChannelCorrespondingcase
  • Can be sent and received when it does not existChannelWhen executingdefaultstatements in;

Notice:nil channelThe operation on it will be blocked all the time, if notdefault case,onlynil channelofselectWill be blocked all the time.

Summarize

This article introduces some development skills in Go, that is, the syntax sugar of Go. Mastering these can improve our development efficiency. Have you learned it?

For more information about Go language development skills and syntax sugar in Go language, please see the relevant links below