SoFunction
Updated on 2025-03-05

Example of implementation of Go variable initialization

introduction

In Go language, initialization of variables is one of the important operations that often encounter when writing programs.

By initializing variables correctly, we can ensure the correctness and readability of the program and avoid some common mistakes.

Go provides a variety of ways to initialize variables, including using the var keyword, short variable declaration, and providing initial values ​​when declaring.

This article will introduce how to initialize variables in Go and provide some examples to better understand these concepts.

Give an example

In Go, variable initialization can be declared using the var keyword or short variable, and variables can be declared and initialized at the same time. Here are some examples:

1. Declare and initialize variables using the var keyword:

var x int = 10
var str string = "Hello, world!"

2. Use short variable declaration method:

x := 10
str := "Hello, world!"

3. Multiple variables are declared and initialized at the same time:

var a, b, c int = 1, 2, 3
var str1, str2 string = "foo", "bar"

4. If the initial value is not provided when declaring a variable, the variable will be initialized to a zero value of its type:

var num int  // num is initialized to 0var str string  // str is initialized to an empty string ""

5. Inside the function, you can initialize the variable in a short variable declaration:

func main() {
    x := 10
    str := "Hello, world!"
    (x, str)
}

In Go, variable initialization is a common and important operation that makes the code clearer and easier to understand and helps avoid potential errors.

Multiple variables are assigned simultaneously

In Go, multiple variables can be used to assign values ​​at the same time. This can be achieved by using a short variable declaration or using an assignment statement.

1. Use a short variable declaration method:

x, y := 10, 20
str1, str2 := "Hello", "World"

2. Use the assignment statement to assign values ​​to multiple variables at the same time:

var a, b int
a, b = 10, 20

3. Exchange the values ​​of two variables:

x, y := 10, 20
x, y = y, x // exchangexandyValue of

4. Declare and assign multiple variables at the same time:

var (
    name  = "John"
    age   = 30
    email = "john@"
)

The Go language also provides two built-in functions, new and make, specifically for data initialization.

1. Built-in function new

The new function is used to allocate memory for values. Unlike new in other programming languages ​​such as Java, it does not initialize the allocated memory, but only clears it. Therefore, when the expression new(T) is evaluated, what is done is to allocate and clear a piece of memory space for a new value of type T, and then return the address of this piece of memory space as the result. And this result is the pointer value to this new T-type value. Its type is *T. In fact, the *T value returned by this new function will always point to a zero value of type T.

For example, the evaluation result of the calling expression new(string) points to a zero value of type "" of string, while the evaluation result of the calling expression new([3]int) points to a zero value of type [3]int{0, 0, 0}.

It is precisely because of this clean memory allocation strategy that we can use the new value of a certain data type immediately after using the built-in function new, without worrying about leaving some initialization traces in this value.

Below we will introduce the following using the structure type Buffer in the standard library code package bytes as an example:

is a byte buffer with variable size. Its zero value is an instantly available empty buffer. Therefore, the evaluation result of calling the expression new() is a pointer value to an empty buffer. Then, we can immediately read and write operations on this buffer.

The structure type Mutex in the standard library code package sync is also a data type that can be used after new. Its zero value is a mutex that is in an unlocked state.

This feature of the built-in function new provides us with a referenceable design rule for custom data types:

For example, when we customize a structure type, we must consider that when the values ​​of each field are zero values ​​of the corresponding type, the structure value should already be available. In this way, when we new it, we can get a pointer value of an immediately available value without additional initialization.

Of course, when it feels that a type of zero value cannot make it available, we can use the corresponding literal to achieve the purpose of allocating memory space and initializing the value.

As we have already mentioned earlier, we can flexibly specify the value of each element in the new value in the literal. But be aware that the literal represents the value of the type, not the pointer value to the value of the type. Therefore, when we replace them with the calling expression that calls the new function, we also need to prefix the literal to represent the pointer value to the value of this type.

2. Built-in function make

Unlike new, the make function can only be used to create values ​​for slice types, dictionary types, and channel types, and return a value of the corresponding type that has been initialized (i.e. non-zero values).

Then why does make function do this?

In previous blog posts, we introduced that these 3 composite types are all reference types. A reference to the underlying data structure value is maintained within each of their values. If their values ​​are not initialized, the reference relationship in them will not be established, and the relevant internal values ​​will also be incorrect. In this case, values ​​of this type cannot be used, because they are incomplete and are still in an unready state. This means that when creating these 3 reference types values, the two steps of memory space allocation and data initialization must be bound together. It is also to ensure the availability of these values ​​that the zero values ​​of slice type, dictionary type and channel type are nil, not the uninitialized value. Therefore, when we new these 3 reference types and want to create their values, we get a pointer value to the null value nil.

In addition, the parameters accepted by the built-in function make are also different from the new function. In addition to accepting a type literal representing the target type, the make function will also accept one or two additional parameters.

For slice types, we can pass the length and capacity of the new value to the make function as well. For example:

// Calling the expression creates a new value of type []int, with a length of 10 and a capacity of 100make([]int, 10, 100)

Of course, we can also omit the last parameter, that is, the capacity of the new value is not specified. In this case, the capacity of this value will be consistent with its length. Examples are as follows:

// The type of variable s is []int, and the length and capacity are both 10.s := make([]int,10)

In the process of initializing a slice value using the make function, the value refers to an array value with the same length as its capacity and the same element type as its element type. This array value is the underlying array of the slice value. Each element in the array value is a zero value of the current element type. However, the slice value will only show elements with the same number of elements as its length. Therefore, the value created and initialized by calling the expression make([]int, 10, 100) is []int{0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}.

When we use the make function to create a value of a dictionary type, we can also specify the length of its underlying data structure. However, this dictionary value will only show the key-value pairs we explicitly put in. For example:

// Call the following expression, the created and initialized value will be map[string]intmake(map[string]int,100)

// You can also ignore the parameter used to represent the length of the underlying data structuremake(map[ string]int)

Although we can also ignore the parameter used to represent the length of the underlying data structure, it is still recommended:

In performance-sensitive application scenarios, this length parameter should be carefully set based on the number of key-value pairs that this dictionary value may contain and the time it is placed in them.

Finally, we briefly introduce the following data initialization for the value of channel type (Channel). So far, we have not explained any channel types. However, in subsequent blog posts, we will explain this special citation type in detail.

// Create a channel type value using make functionmake(chan int, 10)

The first parameter represents the type of the channel, while the second parameter represents the length of the channel. The second parameter can also be ignored as the dictionary type. However, ignoring its meaning is very different from the situation for dictionary types, which we will explain specifically in the blog post later.

Note: The make function can only be applied to the creation of referenced values. And, its result is a value of the type represented by the first parameter, rather than a pointer value to this value.

If we want to get the pointer value, we can only apply the address operator & on the evaluation result of the expression calling the make function, like this:

m := make(map[string]int, 100)
mp := &m

So far, we have introduced 3 ways to create values:

  • Use literal
  • Call built-in function new
  • Calling the built-in function make

They are suitable for different application scenarios. Of course, in some application scenarios, we can have multiple choices:

  • When creating a value of slice type, we can use both literals and make functions. The result of this choice often depends on whether we need to customize a certain element value in the slice value.
  • If we can ensure that a structure type value can be available if the values ​​of the fields are all zero, then using the new function to initialize it is basically equivalent to using literals for initialization. However, it should be noted that the types of results produced by these two methods are different.

Summarize

Through the introduction of this article, I believe that I have already understood the basic methods and precautions for variable initialization in Go language.

Whether you declare and initialize variables using the var keyword, or by short variable declaration, you can easily initialize variables and start writing Go programs.

It is important to always ensure that variables are initialized correctly when writing code, which contributes to the clarity and maintainability of the code.

This is the end of this article about the implementation example of Go variable initialization. For more relevant Go variable initialization content, please search for my previous article or continue browsing the related articles below. I hope everyone will support me in the future!