function
Function declaration
-
Each function declaration contains a name, a parameter list, an optional return list, and the function body:
func name(parameter-list) (result-list) { body } Copy the code
-
The parameter list specifies the parameter names and parameter types of a set of local variables passed in from arguments supplied by the caller.
-
The return list specifies the type of value returned by the function.
recursive
- Functions can be called recursively, meaning that functions can call themselves directly or indirectly
- Many programming languages use fixed-length function call stacks, and the depth of recursion is limited by the size of the fixed-length stack, so you must be careful of stack overflow when making deep recursion calls. But Go implements variable-length stacks that grow with use, up to an upper limit of around 1GB
The return value more
-
A function can return more than one result.
func findLinks(url string) ([]string, error) { / / content } Copy the code
-
Calling a function that involves multi-valued evaluation returns a set of values. If the caller wants to use these return values, it must explicitly assign them to the variable
links, err := findLinks(url) Copy the code
error
- When a function call sends an error it returns an additional result as the error value, which is customarily returned as the last result. If there is only one case of error, the result is usually set to Boolean
- More often than not, especially for I/O operations, the cause of the error can vary and the caller needs some detailed information, in which case the result type of the error is usually error
- An error may be null or non-null. A null value means success and not a null value means failure, and a non-null Error type has an Error message string that can be printed directly by calling its Error method, either fmt.println (err) or fmt.printf (“%v”, err)
Error handling strategy
-
When a function call returns an error, it is the responsibility of the caller to check for the error and take appropriate action.
-
- The most common is passing errors on
resp, err := http.Get(url) iferr ! =nil { return nil, err } Copy the code
-
Build a new error message
iferr ! =nil { return nil, fmt.Errorf("parsing %s as HTML: %v", url, err) } Copy the code
We keep adding additional context information to the original error message to build up a readable description of the error. When errors are finally handled by the program’s main function, it should provide a clear causal chain from the root problem to the overall failure
-
- For unfixed or unpredictable errors, it makes sense to retry the operation after a short interval, and exit after a certain number of retries and a certain amount of time
func WaitForServer(url string) error { const timeout = 1 * time.Minute deadline := time.Now().Add(timeout) for trier := 0; time.Now().Before(deadline); trie++ { _, err := http.Head(url) if err == nil { return nil / / success } log.Printf("server not responding (%s); retrying...", err) time.Sleep(time.Second << uint(tries)) // Index retreat strategy } return fmt.Errorf("server %s failed to respond after %s", url, timeout) } Copy the code
-
- If things still don’t work out, the caller can print an error and gracefully stop the program, but generally this handling should be left to the main program. Normally, library functions should pass errors to the caller unless the error represents an internal consistency error, which means there is a bug inside the library
-
- In some error cases, only the error message is recorded and the program continues. Again, you can choose to use
log
Package to add the usual log prefix and omit the date and time.
iferr := Ping(); err ! =nil { log.Printf("ping failed: %v; networking disabled\n", err) } Copy the code
- In some error cases, only the error message is recorded and the program continues. Again, you can choose to use
-
- In some rare cases we can safely ignore the entire log
dir, err := ioutill.TempDir(""."scratch") iferr ! =nil { return fmt.Errorf("failed to create temp dir: %v", err) } / /... Using temporary directories... os.RemoveAll(dir) $TMPDIR will be deleted periodically if errors are ignored Copy the code
Here, the call to OS.removeall may fail, but the program ignores the error. We intentionally threw the error away, but the program logic looks as if we forgot to handle it. Get used to allowing for errors in each function call, and clearly state your intent when you intentionally ignore an error.
-
End of file identifier
-
You will always get a distinctive error ———— IO.EOF,
package io import "errors" // EOF is returned when there is no more input var EOF = error.New("EOF") Copy the code
-
The caller can check for this using a simple comparison operation, which in the following loop continuously reads characters from the standard input
in := bufio.NewReader(os.Stdin) for { r, _, err := in.ReadRune() if err == io.EOF { bread // Finish reading } iferr ! =nil { return fmt.Errorf("read failed: %v", err) } / /... The use of r... } Copy the code
The function variables
-
Functions are first-class citizens in Go. Like other values, function variables have types, and they can be assigned to or passed to or returned from other functions. Function variables can be called just like any other function, for example:
func square(n int) int { return n * n } func negative(n int) int { return -n } func product(m, n int) int { return m * n } f := square // f is now a function variable fmt.Println(f(3)) / / "9" f = negative fmt.Println(f(3)) / / "- 3" fmt.Printf("%T\n", f) // "func(int) int" f = product Func (int, int) int cannot be assigned to func(int) int Copy the code
-
The zero value of a function type is nil, and calling an empty function variable will cause downtime
var f func(int) int f(3) // Down: call an empty function Copy the code
-
Function variables can be compared to null values:
var f func(int) int iff ! =nil { f(3)}Copy the code
-
However, function variables themselves are not comparable, so they cannot be compared to each other or appear as key values in a map
-
Function variables allow functions to not only parameterize data, but also pass the function’s behavior as arguments, such as the strings.map method in the standard library:
func Map(mapping func(rune) rune.s string) string{... }Copy the code
The first parameter to this method is a function that can be called as follows
func add1(r rune) rune { return r + 1 } fmt.Println(strings.Map(add1, "HAL-9000")) // "IBM.:111" // The second argument is executed according to the behavior of the function passed in with the first argument Copy the code
Anonymous functions
-
Named functions can only be declared in scope at the package level, but we can specify function variables in any expression using function literals
-
Function literals are like function declarations, but without the function name after the func keyword. One of its expressions, its value is called an anonymous function
strings.Map(func(r rune) rune { return r + 1}, "HAL-9000") Copy the code
-
More importantly, functions defined in this way capture the entire lexical environment