
Functions are organized, reusable blocks of code used to perform specified tasks.
Go language supports functions, anonymous functions and closures, and functions belong to "first-class citizens" in go language.
Function definition
func keyword is used for functions defined in Go language. The specific format is as follows:
func Function name(parameter)(Return value){ Function body }
Of which:
- Function declaration: keyword func
- Function name: composed of letters, numbers and underscores. However, the first letter of a function name cannot be a number. Function names cannot be duplicated within the same package.
- Function parameters: parameters are composed of parameter variables and types of parameter variables. Parameter variables can be omitted. There can be one parameter, multiple parameters, or none; Multiple parameters are used to separate; When there are multiple parameters, the parameter variables are either all written or all omitted; If multiple adjacent parameters have the same type, you can only keep the declaration of the last parameter of the same type.
- Function return value: the return value consists of the return value variable and its variable type. The return value variable can be omitted. There can be one return value, multiple return values, or none; Multiple return values must be wrapped in () and separated by; When there are multiple return values, the return value variables are either written or omitted.
- Function body: the logic that implements the specified function.
Define a function that sums two numbers:
func add(x int, y int) int { return x + y }
The parameters and return values of the function are optional. Implement a function that requires neither parameters nor return values:
func printf() { fmt.Println("printf function") }
Function call
After the function is defined, the function can be called by the function name (). Call the two functions defined above:
func main() { printf() ret := add(10, 20) fmt.Println(ret) }
Note that when you call a function with a return value, you can not receive its return value.
parameter
Parameter use
Formal parameter: when defining a function, it is used to receive external incoming data. It is called formal parameter, or formal parameter for short.
Actual parameter: when calling a function, the actual data passed to the formal parameter is called the actual parameter, which is called the actual parameter for short.
Function call:
A: Function names must match B: Arguments and formal parameters must correspond to each other one by one: order, number and type
Type abbreviation
In the parameters of the function, if the types of adjacent variables are the same, the types can be omitted, for example:
func add(x, y int) int { return x + y }
If the types of multiple adjacent parameters are the same, you can only keep the declaration of the last parameter of the same type. The add function has two parameters, and the types of both parameters are int. therefore, you can omit the type of x, because there is a type description after y, and the x parameter is also of this type.
Variable parameters
The number of parameters of the function is variable, such as the most common fmt.Println function. Variable parameters in Go language are identified by adding... After the parameter name. Variable arguments are slice types in the function body
Note: variable parameters are usually used as the last parameter of the function.
for instance:
func add(x ...int) int { fmt.Println(x) //x is a slice sum := 0 for _, v := range x { sum = sum + v } return sum }
Call the above function:
ret1 := add() ret2 := add(10) ret3 := add(10, 20) ret4 := add(10, 20, 30) fmt.Println(ret1, ret2, ret3, ret4) //0 10 30 60
When a fixed parameter is used with a variable parameter, the variable parameter should be placed after the fixed parameter. The example code is as follows:
func add(x int, y ...int) int { fmt.Println(x, y) sum := x for _, v := range y { sum = sum + v } return sum }
Call the above function:
ret5 := add(100) ret6 := add(100, 10) ret7 := add(100, 10, 20) ret8 := add(100, 10, 20, 30) fmt.Println(ret5, ret6, ret7, ret8) //100 110 130 160
In essence, the variable parameters of a function are implemented by slicing.
Parameter transfer
The parameters of go language functions also exist value passing and reference passing
pass by value
func main(){ /* Declare function variables */ getSquareRoot := func(x float64) float64 { return math.Sqrt(x) } /* Use function */ fmt.Println(getSquareRoot(9)) }
Reference passing (involving pointer knowledge points)
This involves the so-called pointer. We know that variables are stored at a certain address in memory, and modifying variables is actually modifying the memory at the variable address. Only when the add1 function knows the address of the x variable can the value of the x variable be modified. Therefore, you need to pass the address & x of x into the function, and change the parameter type of the function from int to * int, that is, to pointer type, so as to modify the value of x variable in the function. At this time, the parameter is still passed by copy, but the copy is a pointer:
//A simple function that implements the operation of parameter + 1 func add1(a *int) int { // Please note that, *a = *a+1 // Modified the value of a return *a // Return new value } func main() { x := 3 fmt.Println("x = ", x) // Should output "x = 3" x1 := add1(&x) // Call ADD1 (& x) to pass the address of X fmt.Println("x+1 = ", x1) // Should output "x+1 = 4" fmt.Println("x = ", x) // Should output "x = 4" }
- Passing pointers enables multiple functions to operate on the same object.
- The pointer is relatively lightweight (8 bytes). It only passes the memory address. You can use the pointer to pass large structures. If the parameter value is passed, a relatively large amount of system overhead (memory and time) will be spent on each copy. Therefore, when passing large structures, using pointers is a wise choice.
- The implementation mechanisms of slice and map in Go language are similar to pointers, so they can be passed directly instead of passing the pointer after taking the address. (Note: if the function needs to change the length of slice, it still needs to take the address and pass the pointer)
Return value
In the Go language, the return value is output through the return keyword.
Multiple return values
Functions in Go language support multiple return values. If a function has multiple return values, all return values must be wrapped with ():
func swap(x, y string) (string, string) { return y, x }
Return value naming
When defining a function, you can name the return value, directly use these variables in the function body, and finally return through the return keyword:
func SumAndProduct(a, b int) (add int, multiplied int) { add = a + b multiplied = a * b return }
Return value supplement
When the return value type of a function is slice, nil can be regarded as a valid slice. There is no need to display and return a slice with length of 0.
func someFunc(x string) []int { if x == "" { return nil // There is no need to return [] int{} } ... }
Blank identifier
_ Is a blank identifier in Go. It can replace any value of any type.
For example, the result returned by the rectProps function is area and perimeter. If you want only area but not perimeter, you can use a blank identifier:
func rectProps(length, width float64) (float64, float64) { var area = length * width var perimeter = (length + width) * 2 return area, perimeter } func main() { area, _ := rectProps(10.8, 5.6) // perimeter is discarded fmt.Printf("Area %f ", area) }
Function advanced order
Variable scope
Scope: the scope within which variables can be used.
global variable
A global variable is a variable defined outside a function and is valid throughout the program's running cycle. All functions can be used and share this data.
//Define global variable num var num int64 = 10 func testGlobalVar() { fmt.Printf("num=%d\n", num) //The global variable num can be accessed in the function } func main() { testGlobalVar() //num=10 }
local variable
Local variables are divided into two types: where variables are defined, they can only be used in which range. Beyond this range, variables are destroyed:
func testLocalVar() { //Define a function local variable x, which takes effect only within the function var x int64 = 100 fmt.Printf("x=%d\n", x) } func main() { testLocalVar() fmt.Println(x) // The variable x cannot be used at this time }
If the local variable and the global variable have the same name, the local variable is accessed first.
package main import "fmt" //Define global variable num var num int64 = 10 func testNum() { num := 100 fmt.Printf("num=%d\n", num) // Local variables are preferred in functions } func main() { testNum() // num=100 }
Variable defined by statement block: this method is usually used to define variables on if conditional judgment, for loop and switch statements.
func testLocalVar2(x, y int) { fmt.Println(x, y) //The parameters of the function also take effect only in this function if x > 0 { z := 100 //The variable z takes effect only in the if statement block fmt.Println(z) } //fmt.Println(z) / / variable Z cannot be used here }
The variables defined in the for loop statement also take effect only in the for statement block:
func testLocalVar3() { for i := 0; i < 10; i++ { fmt.Println(i) //The variable i takes effect only in the current for statement block } //fmt.Println(i) / / variable i cannot be used here }
Function types and variables
Define function type
You can use the type keyword to define a function type. The specific format is as follows:
type calculation func(int, int) int
The above statement defines a calculation type, which is a function type. This function receives two parameters of type int and returns a return value of type int.
In short, all functions that meet this condition are of type calculation. For example, the following add and sub are of type calculation.
func add(x, y int) int { return x + y } func sub(x, y int) int { return x - y }
Both add and sub can be assigned to variables of type calculation.
var c calculation c = add
Function type variable
Declare a variable of function type and assign a value to it:
func main() { var c calculation // Declare a variable of type calculation c c = add // Assign add to c fmt.Printf("type of c:%T\n", c) // type of c:main.calculation fmt.Println(c(1, 2)) // Call c like add f := add // Assign the function add to the variable f1 fmt.Printf("type of f:%T\n", f) // type of f:func(int, int) int fmt.Println(f(10, 20)) // Call f as you call add }
Higher order function
Higher order functions are divided into two parts: functions as parameters and functions as return values.
Function as parameter
Functions can be used as arguments:
func add(x, y int) int { return x + y } func calc(x, y int, op func(int, int) int) int { return op(x, y) } func main() { ret2 := calc(10, 20, add) fmt.Println(ret2) //30 }
Function as return value
Functions can also be used as return values:
func do(s string) (func(int, int) int, error) { switch s { case "+": return add, nil case "-": return sub, nil default: err := errors.New("Unrecognized operator") return nil, err } }
Anonymous functions and closures
Anonymous function
Of course, functions can also be used as return values, but in Go language, functions can no longer be defined inside functions as before, only anonymous functions can be defined. Anonymous functions are functions without function names. The definition format of anonymous functions is as follows:
func(parameter)(Return value){ Function body }
Anonymous functions cannot be called like ordinary functions because they do not have a function name. Therefore, anonymous functions need to be saved to a variable or used as immediate execution functions:
func main() { // Save anonymous functions to variables add := func(x, y int) { fmt.Println(x + y) } add(10, 20) // Calling anonymous functions through variables //Self executing function: anonymous function definition plus () is executed directly func(x, y int) { fmt.Println(x + y) }(10, 20) }
Anonymous functions are mostly used to implement callback functions and closures.
closure
A closure is an entity composed of a function and its associated reference environment. Simply put, closure = function + reference environment:
func adder() func(int) int { var x int return func(y int) int { x += y return x } } func main() { var f = adder() fmt.Println(f(10)) //10 fmt.Println(f(20)) //30 fmt.Println(f(30)) //60 f1 := adder() fmt.Println(f1(40)) //40 fmt.Println(f1(50)) //90 }
When the variable f is a function and it references the X variable in its external scope, f is a closure. The variable x is also valid throughout the life cycle of F. Advanced closure example 1:
func adder2(x int) func(int) int { return func(y int) int { x += y return x } } func main() { var f = adder2(10) fmt.Println(f(10)) //20 fmt.Println(f(20)) //40 fmt.Println(f(30)) //70 f1 := adder2(20) fmt.Println(f1(40)) //60 fmt.Println(f1(50)) //110 }
Advanced closure example 2:
func makeSuffixFunc(suffix string) func(string) string { return func(name string) string { if !strings.HasSuffix(name, suffix) { return name + suffix } return name } } func main() { jpgFunc := makeSuffixFunc(".jpg") txtFunc := makeSuffixFunc(".txt") fmt.Println(jpgFunc("test")) //test.jpg fmt.Println(txtFunc("test")) //test.txt }
Advanced closure example 3:
func calc(base int) (func(int) int, func(int) int) { add := func(i int) int { base += i return base } sub := func(i int) int { base -= i return base } return add, sub } func main() { f1, f2 := calc(10) fmt.Println(f1(1), f2(2)) //11 9 fmt.Println(f1(3), f2(4)) //12 8 fmt.Println(f1(5), f2(6)) //13 7 }
Closures are not complicated. Just remember that closures = functions + references to outer variables.
defer statement
The defer statement in the Go language delays the statements that follow it. When the function to which defer belongs is about to return, the delayed statements are executed in the reverse order defined by defer, that is, the statements first deferred are executed last, and the statements last deferred are executed first.
func main() { fmt.Println("start") defer fmt.Println(1) defer fmt.Println(2) defer fmt.Println(3) fmt.Println("end") }
Output results:
start end 3 2 1
Due to the delayed calling of defer statement, defer statement can easily deal with the problem of resource release. For example, resource cleaning, file closing, unlocking and recording time.
defer execution timing
In the function of Go language, the return statement is not an atomic operation at the bottom. It is divided into two steps: assigning a return value and RET instruction. The defer statement is executed after the return value assignment operation and before the RET instruction is executed. See the following figure for details:
[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-4i604a1l-1634525193071)( https://qiniu.drunkery.cn//blogimage-20210930113047536.png )]
defer classic case
Read the following code and write the final print result.
func f1() int { x := 5 defer func() { x++ }() return x } func f2() (x int) { defer func() { x++ }() return 5 } func f3() (y int) { x := 5 defer func() { x++ }() return x } func f4() (x int) { defer func(x int) { x++ }(x) return 5 } func main() { fmt.Println(f1()) //5 fmt.Println(f2()) //6 fmt.Println(f3()) //5 fmt.Println(f4()) //5 }
defer interview questions
func calc(index string, a, b int) int { ret := a + b fmt.Println(index, a, b, ret) return ret } func main() { x := 1 y := 2 defer calc("AA", x, calc("A", x, y)) x = 10 defer calc("BB", x, calc("B", x, y)) y = 20 }
Q: what is the output of the above code? (Note: when defer registers a function to be delayed, all parameters of the function need to be determined)
defer note
When the statements in the peripheral function are executed normally, the peripheral function will really end the execution only when all the delay functions are executed. When executing in a peripheral function return Statement, the peripheral function will not really return until all the delay functions are executed. When the code in the peripheral function causes a run-time panic, the run-time panic will not be really extended to the calling function until all the delay functions are executed.
Introduction to built-in functions
Built in function | introduce |
---|---|
close | It is mainly used to close the channel |
len | It is used to find the length, such as string, array, slice, map and channel |
new | It is used to allocate memory. It is mainly used to allocate value types, such as int and struct. The pointer is returned |
make | It is used to allocate memory. It is mainly used to allocate reference types, such as chan, map and slice |
append | Used to append elements to arrays and slice s |
panic and recover | Used for error handling |
panic/recover
At present (Go1.12), there is no exception mechanism in the Go language, but the panic/recover mode is used to handle errors. Panic can be raised anywhere, but recover is only valid in the function called by defer. Let's start with an example:
func funcA() { fmt.Println("func A") } func funcB() { panic("panic in B") } func funcC() { fmt.Println("func C") } func main() { funcA() funcB() funcC() }
Output:
func A panic: panic in B goroutine 1 [running]: main.funcB(...) .../code/func/main.go:12 main.main() .../code/func/main.go:20 +0x98
During program running, panic was thrown in funcB, resulting in program crash and abnormal exit. At this time, you can recover the program through recover and continue to execute it later.
func funcA() { fmt.Println("func A") } func funcB() { defer func() { err := recover() //If a panic error occurs in the program, it can be recovered through recover if err != nil { fmt.Println("recover in B") } }() panic("panic in B") } func funcC() { fmt.Println("func C") } func main() { funcA() funcB() funcC() }
be careful:
- recover() must be used with defer.
- The defer must be defined before the statement that may cause panic.
