Basic grammar in one day: go 2

Time:2022-4-27

abstract

After configuring the environment, we need to study the syntax of the language. In this article, the author hopes to briefly introduce various grammars of golang and make some simple comparisons with C and Java to deepen his memory. Because this article is only the second article on the introduction of golang, this article will not dig deeply into some instructions, but just stay at the level of “how to use”. As for “why”, it involves specific application scenarios and assembly instructions, which will be introduced by the author in future articles.

1 Guide Package

As we all know, “Hello world” is a sense of ceremony for programmers.

The “hello” method will be related to the “world” input. Therefore, how to import packages is the first step we need to study.

In C language, we use include, and in Java, we use import. The same is true in golang. We use import to introduce other packages. In the previous article, we mentioned that for imported packages, the compiler will first look for them in goroot, then in the gopath corresponding to the project, and finally in the global gopath. If they are not found, the compiler will report an error.

Note that there is a big difference between golang and Java, that is, in golang, import imports directories instead of package names. Moreover, golang does not force the package name and directory name to be consistent.

Here are some examples to illustrate the relationship between package name and directory in golang. Let’s take a look at the directory structure first:

As you can see, we set two folders under SRC and two go files under the second folder.
Let’s take a look at the code of these two files, test1 Go as follows:

package pktest

func Func1()  {
	Println ("this is the first function")
}

test2. Go as follows:

package pktest

func Func2()  {
	Println ("this is the second function")
}

Then let’s look at testmain Go to the following:

package main

import "package1/package2"

func main() {
	pktest.Func1()
}

Notice that when we call func1, we use pktest instead of package2 in Package1 / package2.

According to our thinking in Java, we should use package 2 Func1’s calling method or test1 Func1 is such a method.

This is because in golang, there is no mandatory requirement for the package name to be consistent with the directory name. In other words, in the above example, the folder name in our reference path is package2, while the package names of the two files under this folder are set to pktest. In the reference of golang, we need to fill in the relative path of the source file.

In other words, we can understand that package name and path are actually two concepts. The file name will not be explicitly referenced in golang. The usual reference format is packagename FunctionName。

The conclusions are as follows:

  • Import imports the relative path of the source file, not the package name.
  • It is customary to ensure that the package name and directory name are consistent, but this is not mandatory (but it is not recommended to do so, which is easy to cause the person calling the package to be unable to quickly know what the package name is)
  • When referring to members within a package in code, use the package name instead of the directory name.
  • There can only be one package name in a folder, and there are no other restrictions on the name of the source file.
  • If multiple folders have packages with the same name, they are actually unrelated packages.

The above part is taken from this article

2 declaration

After reading the guide package, let’s see how to declare a variable. In the part of declaring variables, it is also quite different from C and Java.

2.1 definition of variables

Let’s define some variables first:

var a int
var b float32
var c, d float64
e, f := 9, 10
var g = "Ricardo"

We can see that to define a variable in golang, we need to use the VaR keyword. Unlike C or Java, we need to write the type of the variable after the variable name. Moreover, in golang, we are allowed to define multiple variables at one time and assign values at the same time.

Another way is to use the symbol: = as well. After using this symbol, developers no longer need to write the VaR keyword. They just need to define the variable name and assign a value later. Moreover, the golang compiler will automatically deduce the type of the variable according to the type of the following value.

In the process of variable definition, if the initial value of the variable is given at the time of definition, there is no need to declare the type of the variable, such as variable G.

Note that golang is a strongly typed language. All variables must have types, and variables can only store specific types of data.

2.2 anonymous variables

The variable with identifier (underlined) is an anonymous variable reserved by the system. It will be released immediately after assignment, which is called anonymous variable. Its function is a variable placeholder and a structure for assigning values to its variables. It is usually used in batch assignment.
For example, if a function returns multiple values and we only need some of them, we don’t need to use them to occupy bits

func main() {
  //To call the function, only the second return value is required. The first and third use anonymous variable placeholders
  _, v, _ := getData()
  fmt.Println(v)
}
//Function that returns two values
func getData() (int, int, int) {
  //Return 3 values
  return 2, 4, 8
}

As shown in the above code, if I only need the value of a variable, I don’t need to define some meaningless variable names. I just need to use placeholders, which are anonymous variables of “burn after use”.

2.3 constants

In the constant definition of golang, const keyword is used, and: = identifier cannot be used.

3 judgment

When using Java or C, we write judgment statements as follows:

if(condition){
    ...
}

The only difference in golang is that curly braces are not required, but curly braces are still required. As follows:

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
	    return v
	}
	return lim
}

In addition to not having to write parentheses, golang also allows a simple statement to be executed with a semicolon before judging the condition; separate.

4 cycle

In golang, there is only one loop, the for loop.

Like the judgment statement, there are no parentheses in golang.

func main() {
	sum := 0
	for i := 0; i < 10; i++ {
		sum += i
	}
	fmt.Println(sum)
}

In addition, in the loop condition, the initialization statement and post statement are optional. At this time, remove the semicolon and the for loop becomes a while loop.

func main() {
	sum := 1
	for sum < 1000 {
		sum += sum
	}
	fmt.Println(sum)
}

Moreover, if the loop condition is omitted, the loop will not end. Therefore, the infinite loop can be written very compactly. At this time, the effect is the same as that of while (true).

func main() {
	for {
	    ...
	}
}

5 function

5.1 definition of function

In the function definition of golang, all functions begin with func, and the hump naming method is recommended for the naming of golang.

Note that in the function of golang, if the initial letter is lowercase, it can only be used in the package; If the initial letter is capitalized, it can be introduced outside the package. It can be understood that functions in lowercase are private and functions in uppercase are public.

In the function definition of golang, you can either not accept parameters or accept multiple parameters. In the process of defining parameters, the variable name is defined first and then the variable type is declared according to the format of defining variables. For the return type of a function, write the function name first and then the return type according to this format:

func add(x int, y int) int {
	return x + y
}

func main() {
	fmt.Println(add(42, 13))
}

Moreover, for two parameters of the same type, only one parameter type can be written. The usage is as follows:

func add(x, y int) int {
	return x + y
}

In golang, the return value of a function is different from C and Java.

Functions in golang can return any number of return values.

For example, the little plum below,

func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	a, b := swap("hello", "world")
	fmt.Println(a, b)
}

Secondly, the return value of the function can be named:

func split(sum int) (x, y int) {
	x = sum * 4 / 9
	y = sum - x
	return
}

Here, we can understand that these variable values are predefined at the top of the function, while an empty return statement returns all defined return variables by default.

5.2defer

In golang, there is a keyword called defer.

The defer statement delays the execution of the function until the outer function returns. The parameters of a deferred function are evaluated immediately, but the function will not be called until the outer function returns.

func main() {
	defer fmt.Println("world")

	fmt.Println("hello")
}

In this code, the original execution path is from top to bottom, that is, first output “world”, and then output “hello”. However, due to the existence of the keyword defer, this line of statement will be executed at the end, resulting in the effect of printing “hello” first and then “world”.

Note that defer must be followed by a function call statement, not other statements, otherwise the compiler will report an error.

The scenario that can be considered is the closing of files or the release of database connections. In this way, the opening and closing codes can be written together, which can not only make the code cleaner, but also prevent developers from forgetting to close after writing long business codes.

As for the underlying implementation of defer, this article will not explain in detail. In short, it is to press the address of the function call behind the defer statement into a stack. After the current function is executed and the CPU is about to execute the next line of code outside the function, pop up the instruction address in the stack to the CPU for execution. Do not end this function until the stack is empty and continue to execute the following code.

It can also be inferred from the above statement that if there are multiple refer statements, they will be executed from bottom to top.

Because this article is only a simple comparison of various instructions, the detailed explanation of refer will be described in later articles.

6 pointer

If you are a C or C + + developer, you must be familiar with pointers; For Java developers, pointers are transparent to developers. An object will occupy a certain memory space in the heap. In the current stack frame, there is a local variable whose value is the first address of that object, which is also a pointer.

It can be said that the pointer is a way for developers to access memory, but whether the control is handed over to developers or virtual machines.

In golang, pointers are used in the same way as C. Similarly, use & to get the address and * to get the value in the address.

However, unlike C, golang does not have pointer operations.

7 array

In golang, the definition of array is as follows:

var a [10]int

Doing so declares variable a as an array of 10 integers.

Note that in golang, the size of the array is the same as that of C language, and cannot be changed.

7.1 slicing

Array slicing, as the name suggests, is to cut out the required part of an array on demand.

The size of each array is fixed. Slicing provides dynamic size and flexible perspective for array elements. In practice, slices are more commonly used than arrays.

Slices are defined by two subscripts, an upper bound and a lower bound, separated by colons:

a[low : high]

It selects a half open interval, including the first element, but excluding the last element.

The following expression creates a slice that contains elements with subscripts from 1 to 3 in a:

a[1:4]

for instance:

func main() {
	str := [4]string{
	    "aaa",
	    "bbb",
	    "ccc",
	    "ddd",
	}
	fmt.Println(str)

	a := str[0:2]
	b := str[1:3]
	fmt.Println(a, b)

	b[0] = "XXX"
	fmt.Println(a, b)
	fmt.Println(str)
}

We define an array containing four elements: “AAA”, “BBB”, “CCC” and “DDD”. Then we define two slices, a and B. according to the definition, a is “AAA” and “BBB”, B is “BBB” and “CCC”.

At this time, we change B [0] to “XXX”, then B becomes “XXX” and “CCC”, which is beyond doubt. But contrary to intuition, the array STR at this time also becomes “AAA”, “XXX”, “CCC” and “DDD”.

This is because the slice in golang does not copy, but defines a new pointer to the memory space where the original array is located. The value of the array is modified accordingly.

In addition, slices can add elements with append. However, if the capacity of the underlying array is insufficient at this time, the slice will point to an array that will be copied after reallocating space.

Therefore, it can be concluded that:

  • The slice does not store any data, it just describes a segment in the underlying array.
  • Changing the element of a slice modifies the corresponding element in its underlying array.
  • These modifications are observed by slices that share the underlying array with it.

7.2 make

Slices can be created with the built-in function make, which is how you create dynamic arrays.

Before that, we need to explain two definitions, len (length) and cap (capacity).
Len is the length of the array, which refers to the agreed length of the array when it is defined.  
Cap is the capacity of the array. It refers to the length of the underlying array, or the length of the original array in memory.
For the slice mentioned above, if I define a slice of STR [0,0], the length is 0, but the capacity is still 5.

The make function allocates an array whose element is zero and returns a slice that references it:

a := make([]int, 5)  // len(a)=5

To specify its capacity, pass the third parameter to make:

b := make([]int, 0, 5) // len(b)=0, cap(b)=5

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4

The size of the custom slice, that is, the make function. In Java terms, it can be overloaded.

There are two forms. If there are only two parameters, the first parameter is the type of elements in the array, and the second parameter is the length of the array (at this time, the length and capacity are 5).

If there is a third parameter, the third parameter can specify the capacity of the array, that is, how much space the array allocates in memory.