[Translate] effective go Functions

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Functions

Multiple return values returns more than one value

One of Go ' s unusual features is that functions and methods can return multiple values. This form can is used to improve on a couple of clumsy idioms in C Programs:in-band error returns (such as-1 for EOF) an D modifying an argument.

The function and method of go can return multiple values at the same time. This feature can improve the less desirable multi-value return form in C programs-The parameter incoming pointer modifies the parameter with this pointer and returns a value such as 1 or EOF.

In C, a write error was signaled by a negative count with the error code secreted away in a volatile location. In Go, Write can return a count and an error: "Yes, you wrote some bytes but not all of the them because you filled T He device ". The signature of File.write in package OS are:

For example, one of the functions of writing a file in C is as follows, and if it is interrupted by another interrupt, it will return EINTR if it has already written a portion of the data and then an error has occurred then return the number of bytes already written you need to add a condition to determine if the error has actually occurred

#include <unistd.h> ssize_t Write (int fd, const void *buf, size_t count);


However, go can directly return the number of bytes written and the corresponding error message:
Func (file *file) Write (b []byte) (n int, err error)

And as the documentation says, it returns the number of bytes written and a non-nil error when n! = Len (b). This is a common style; See the sections on error handling for more examples.

This function returns the number of bytes written, as described in the document, if the number of bytes written and the B inconsistency err is the corresponding error message


A similar approach obviates the need to pass a pointer to a return value to simulate a reference parameter. Here's a simple-minded function to grab a number from a position in a byte slice, returning the number and the next Positi On.

Similarly, there is a method that does not require an incoming pointer to impersonate a parameter reference the following function takes data from a byte slice and returns the change data and its successor location

Func Nextint (b []byte, I int) (int, int) {for    ; I < Len (b) &&!isdigit (B[i]); i++ {    }    x: = 0    F or; I < Len (b) && isdigit (B[i]); i++ {        x = x*10 + int (b[i])-' 0 '    }    return x, i}

You could use it to scan the numbers a input slice B like this:

You can use the above function to traverse slice:

    For I: = 0; I < Len (b); {        x, i = Nextint (b, i)        FMT. PRINTLN (x)    }


Named result Parameters

The return or result "parameters" of a Go function can be given names and used as regular variables, just like the incomin G parameters. When named, they is initialized to the zero values for their types when the function begins; If the function executes a return statement with no arguments, the current values of the result parameters is used as the Returned values.

Go can define a return value with the same name as the input parameter. When the return value of these return values is initialized to the 0 value of the corresponding type if the function's return statement does not have a parameter, the current value of the return value variable is returned as the return value.

The names is not mandatory but they can make code shorter and Clearer:they ' re documentation. If We name the results of Nextint it becomes obvious which returned int is which.

Of course we can not use the named return value but using the named return value code will be more concise as the following function declaration we can clearly know the meaning of the two return values of value and Nextpost

Func Nextint (b []byte, POS int) (value, Nextpos int) {


Because named results is initialized and tied to a unadorned return, they can simplify as well as clarify. Here ' s a version of IO. Readfull that uses them well:

Since the named return value variable is automatically initialized and returned with no parameters, it will be used with the current value as simple as this example:

Func Readfull (R Reader, buf []byte) (n int, err error) {for    len (BUF) > 0 && Err = = Nil {        var nr int
  NR, err = R.read (buf)        n + = nr        buf = Buf[nr:]    }    return}


Defer deferred execution

Go ' s defer statement schedules a function call (the deferred function) to be run immediately before the function Executing the defer returns. It's an unusual but effective the deal with situations such as resources that must be released regardless of which path A function takes to return. The canonical examples is unlocking a mutex or closing a file.

The defer statement of Go will be called before the function is returned. It's weird, but it's very effective in some situations, like no matter how the function executes its execution path, the resource that gets in the function must be freed, and then you can add a defer statement. Here's a classic example of releasing a lock or closing a file

Contents returns the file ' s Contents as a string.func Contents (filename string) (string, error) {    F, err: = OS. Open (filename)    if err! = Nil {        return "", Err    }    defer f.close ()  //F.close would run when we ' re finishe D.    var result []byte    buf: = Make ([]byte, +) for    {        N, err: = F.read (buf[0:])        result = Append (result, buf[0: N] ...) Append is discussed later.        If err! = Nil {            If err = = Io. EOF {                break            }            return "", err  //F'll be closed if we return to here.        }    }    return string (Result), nil//F'll is closed if we return here.}


Deferring a call to a function such as Close have both advantages. First, it guarantees that you'll never forget to close the file, a mistake that's easy to make if you later edit the fun Ction to add a new return path. Second, it means, the close sits near the open, which was much clearer than placing it at the end of the function.

Putting down a function like close for example can bring two benefits first of all it guarantees that you will not forget to close the file next you can use defer to close the file immediately after opening the file, which is clearer than closing the file at the end of the function.

The arguments to the deferred function (which include the receiver if the function is a method) was evaluated when the defer executes, not at the call executes. Besides avoiding worries about variables changing values as the function executes, this means that a single deferred call Site can defer multiple function executions. Here ' s a silly example.

The arguments passed to the defer function are evaluated when defer executes (if the function being defer accepts the return value of another function, then the inner layer's function is executed at this time) instead of being computed when the defer function executes. This allows you to use defer for multiple functions without worrying about the arguments that are passed to defer's function in the process:

For I: = 0; I < 5; i++ {    defer FMT. Printf ("%d", I)}

Deferred functions is executed in LIFO order, so this code would cause 4 3 2 1 0 to be printed when the function returns. A more plausible example are a simple-to-trace function execution through the program. We could write a couple of simple tracing routines like this:

The execution order of the defer function is LIFO, so the result of the above code is 4 3 2 1 0 look at the following example using the trace function to track the execution of a function:

Func trace (s string)   {fmt. Println ("Entering:", s)}func Untrace (s string) {Fmt. Println ("Leaving:", s)}//use them like This:func A () {    trace ("a")    defer untrace ("a")    //Do something ...}

We can do better by exploiting the fact, arguments to deferred functions is evaluated when the defer executes. The tracing routine can set up the argument to the Untracing routine. This example:

Further we can optimize the code by using the parameters of the defer function to be initialized at the time of defer execution:

Func trace (s string) string {    fmt. Println ("Entering:", s)    return s}func un (s string) {    FMT. Println ("Leaving:", s)}func a () {    defer un (trace ("a"))    FMT. Println ("in a")}func B () {    defer un (trace ("B"))    FMT. Println ("in B")    A ()}func main () {    B ()}

Prints program Run Results:

Entering:bin Bentering:ain aleaving:aleaving:b


For programmers accustomed to block-level resource management from other languages, defer may seem peculiar Interesting and powerful applications come precisely from the fact that it's not block-based but function-based. In the sections on panic and recover we'll see another example of their possibilities.

For programmers who are accustomed to resource management methods in other languages, such as Python, defer looks a little strange, but his charms are based on functions rather than blocks in subsequent panic and recover chapters we will see more defer figure

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.