SoFunction
Updated on 2025-03-05

Commonly used syntactic sugar sharing in Golang

1. Origin of the name

The concept of syntactic sugar was proposed by British computer scientist Peter Landing to represent some type of syntax in a programming language. These syntaxes do not affect functionality but are very convenient to use.

Syntax sugar, also known as sugar syntax, not only does these syntaxes not affect the function, but the compiled results are the same as not using syntax sugar.

Syntax sugar may make code writing simple and may make code readable, but sometimes it will give you an accident and may lead you into a trap to cause problems with your code. This article will explain the commonly used grammatical sugar in Golang.

2. Commonly used syntax sugar in Golang

2.1 Short variable declaration:=

Rules: The syntax sugar of short variable declaration is very convenient to use, which leads to you that you may use it to define a variable. Often the program's bug is written casually. Here I will talk about the principles and rules of short variable declaration.

(1) Multivariate assignment may be redeclared

Use := to declare multiple variables at once, for example:

i, j := 0, 0
j, k := 1, 1

When a new variable exists on the left side of := (such as k), then the declared variable (such as j) will be redeclared. This does not introduce new variables, but changes the value of the variable.

When there is no new variable on the left side of :=, the compile error is reported. In the following example, since there is no new variable on the left side of ':=' " compiled, the " error will be prompted.

i,j := 2,3
i,j := 6,8

(2) Cannot be used outside the function

:= This short variable declaration can only be used in functions, and it is not possible to initialize global variables.

It can be understood as := will be split into two statements, namely declaration and assignment. Assignment statements cannot appear outside the function, because outside any function, statements should start with keywords, such as keywords such as type and var.

For example, like this:

package sugar
import fmt
 
rule := "Short variable declarations" // syntax error: non-declaration statement outside function body

This is because variables declared outside of the function are global variables, and they have package-level scopes. In package-level scopes, declarations of variables are usually explicit and do not require the use of short variables to declare syntactic sugar. And in the declaration of global variables, the type of variable must be specified, because the compiler needs to know the size and layout information of the variables in order to allocate memory to them at compile time.

Therefore, if you want to declare variables at the package level, you need to use the var keyword or the const keyword for explicit declaration, and you cannot use the := syntax sugar. For example:

package main
 
import "fmt"
 
// Use the var keyword to explicitly declare global variablesvar globalVar = 10
 
func main() {
    // Use := syntax sugar to declare local variables inside the function    localVar := 20
    (globalVar, localVar)
}

In short, := can only be used for declaration and initialization of local variables, but not for declaration and initialization of global variables. This is the syntax provision of Go language.

(3) Variable scope problem

Almost all engineers understand the scope of variables, but because := is used too frequently, it is still possible to fall into a trap.

The following code is derived from real projects, but for the sake of convenience of description and to avoid information security risks, it is simplified as follows:

func Redeclare() {
    field, err:= nextField()   // No. 1 err 
    if field == 1{
        field, err:= nextField()     // No. 2 err        newField, err := nextField() // No. 3 err        ...
    }
    ...
}

Note the three err variables declared above. No. 2 err and No. 1 err do not belong to the same scope, := declares a new variable, so No. 2 err and No. 1 err belong to two variables. No. 2 err and No. 3 err belong to the same scope, := Redeclare err but no new variable is created, so No. 2 err and No. 3 err are the same variable. (Repeated assignment of the same variable will be redeclared, which does not introduce new variables, but only changes the value of the variable.)

If you mistakenly confuse No. 2 err with No. 1 err, it is easy to cause unexpected mistakes.

2.2 Variable parameter function...

Let's write a variable parameter function first:

func Greeting(prefix string, who ...string) {
    if who == nil {
        ("Nobody to say hi.")
        return
    }
    for _, people := range who{
        ("%s %s\n", prefix, people)
    }
}

The Greeting function is responsible for greeting the specified person, and its parameter who is a variable parameter. This function almost expresses all the features of the variable parameter function:

Variable parameters must be at the last of the function parameter list (otherwise it will cause compile-time ambiguity);

Variable parameters are parsed as slices inside the function;

Variable parameters can be not filled in, and when they are not filled in, the function is treated as nil slices;

Variable parameters can be filled into slices;

The variable parameters must be of the same type (if required, it can be defined as the interface{} type);

(1) Example of use - no value transmission

When calling a variable parameter function, the variable parameter part can not be passed, for example:

func ExampleGreetingWithoutParameter() {
    ("nobody")
    // OutPut:
    // Nobody to say hi.
}

No second parameter is passed here. If the variable parameters are not passed, the default is nil.

(2) Example of use - Pass multiple parameters

When calling a mutable parameter function, the mutable parameter part can pass multiple values, for example:

func ExampleGreetingWithParameter() {
    ("hello:", "Joe", "Anna", "Eileen")
    // OutPut:
    // hello: Joe
    // hello: Anna
    // hello: Eileen
}

There can be multiple variable parameters. Multiple parameters will generate a slice and pass it in, and the function will be processed as slices internally.

(3) Example of use - Passing slice

When calling a mutable parameter function, the mutable parameter part can directly pass a slice. The parameter part needs to use slice... to represent slices. For example:

func ExampleGreetingWithSlice() {
    guest := []string{"Joe", "Anna", "Eileen"}
    ("hello:", guest...)
    // OutPut:
    // hello: Joe
    // hello: Anna
    // hello: Eileen
}

One thing to note at this time is that when slices are passed in, no new slices are generated, that is, the slices used inside the function share the same storage space as the incoming slice. To put it bluntly, if the slice is modified internally, it may affect the externally called functions.

2.3 new function

In Go, the new function is used to dynamically allocate memory, returning a pointer to the newly allocated zero value. Its syntax is as follows:

func new(Type) *Type

Where Type represents the type of memory to be allocated, and the new function returns a pointer to the newly allocated zero value of type Type. But it should be noted that the new function only allocates memory and returns a pointer to the newly allocated zero value without initializing that memory.

The so-called zero value refers to the default value automatically assigned by variables in Go language when declared. For basic types, their zero values ​​are as follows:

  • Boolean type: false
  • Integer: 0
  • Floating point type: 0.0
  • Plural type: 0 + 0i
  • String: "" (empty string)
  • Pointer: nil
  • Interface: nil
  • Slices, maps, and channels: nil

Therefore, the pointer returned by the new function points to the newly allocated zero value, but does not initialize it to a non-zero value. If you need to initialize memory to a non-zero value, you can use the structure literal or explicitly assign it. For example:

package main
 
import "fmt"
 
type Person struct {
    name string
    age  int
    sex  int
}
 
func main() {
    // Allocation of memory using the new function, but it will not be initialized to a non-zero value    p := new(Person)
    (p) // Output: &{ 0 0} 
    // Initialize using structure literal    p2 := &Person{name: "Tom", age: 18, sex: 1}
    (p2) // Output: &{Tom 18 1} 
    // explicitly assign values ​​to fields    p3 := new(Person)
     = "Jerry"
     = 20
     = 0
    (p3) // Output: &{Jerry 20 0}}

In the above code, a new Person structure is assigned using the new function, but it is not initialized to a non-zero value, so the output is "empty string 0 0". Next, initialize it to a non-zero value using the structure literal or explicitly assign it.

It is obvious that the design of the new function is also for the convenience of programmers.

Note 1: p3 := new(Person) returns a pointer to the zero value of the newly assigned Person type object. According to our understanding of pointer syntax, if the assignment is displayed based on p3, you need to use the following syntax for assignment:

(*p3).name = "Jerry"
(*p3).age = 20
(*p3).sex = 0

When we assign values ​​to pointer-type structure objects, we rarely carry *. This is also a simplification for us by Go pointer syntax sugar. This part will be introduced in detail later.

Note 2: For more details of the new function, please refer to "Go language new( ) function》This blog post.

This is the end of this article about the commonly used grammatical sugar sharing in Golang. For more related grammatical sugar content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!