A brief analysis of defer function parameter evaluation

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

A. Intro

In the book, after the publication of "A brief analysis of the output of a Go language code" article, the original question has a new problem, this is a typical Gopher learning go process, presumably a lot of gopher, including I have encountered. Let's take a look at this code (from the original problem-speaker):

// https://play.golang.org/p/dOUFNj96EIQpackage mainimport "fmt"func main() {    var i int = 1    defer fmt.Println("result =>",func() int { return i * 2 }())    i++}

There's obviously a hole here! The general logic of beginners is generally: defer is executed after the main function exits, before exiting I have done +1 operations, the value becomes 2, so defer after the println should output:result = 4 ! What is the actual output?

result => 2

How is that possible?

In fact, not only defer, that is, using the GO keyword to replace the defer, the output is the same: result= 2:

package mainimport (     "fmt"    "time")func main() {    var i int = 1    go fmt.Println("result =>",func() int { return i * 2 }())    i++    time.Sleep(3*time.Second)}

Two. Defer function analysis

So why is the output 2 instead of 4? Because either the GO keyword or the defer keyword, when the code executes to them, the compiler prepares the parameter stack for the function call for the function that follows them, the parameter value and the parameter type size to determine . In this way, you have to evaluate: Evaluate the parameters of the functions that follow them.

Take the example of the first defer of this article! We need to evaluate the parameters for the function following defer:

defer fmt.Println("result =>",func() int { return i * 2 }())

At this point the function behind defer is println, where println has two input parameters: "result =" and func () int {return I * 2} (), which is a string constant value, and the latter is a function call, We need to evaluate the function call. At this point, I is still 1, so the second parameter of println evaluates to 2, so the above defer call is equivalent to:

defer fmt.Println("result =>",2)

Therefore, regardless of how much the final I value becomes, the final output of defer is:result = 2. This is also true for the arguments that follow the Go keyword. In fact, this process and for the normal function of the call to prepare is the same, you have to evaluate the parameters of the function, and then enter the function body, but defer will enter the function of the execution of the process deferred until the defer caller exit.

After figuring out the defer principle, if we want to output 4 when the defer function executes, a closure function is used:

// https://play.golang.org/p/Eux7zpSr7O8package mainimport "fmt"func main() {        var i int = 1        defer func() {                fmt.Println("result =>", func() int { return i * 2 }())        }()        i++}

Here we see that the defer is followed by an anonymous function with no parameters, so-called parameter evaluation is also no value can be obtained. Before the main function exits, the anonymous function after defer actually executes when the value of I is already 2, so the println output in the closure function is 4.

Three. Defer method analysis

Defer can also use method calls in addition to normal function calls:

defer instance.Method(x,y)

This may be a bit confusing for beginners, most of it is the receiver type of method and go automatically to the instance method call dereference or address problem, we "strike", and then based on the previous article "A Go Language code output of a brief analysis" Make some changes in the example to see what happens when you change the GO keyword to defer:

//https://play.golang.org/p/T8CdRfEn2h4package mainimport (    "fmt")type field struct {    name string}func (p *field) print() {    fmt.Println(p.name)}func main() {    data1 := []*field{{"one"}, {"two"}, {"three"}}    for _, v := range data1 {        defer v.print()    }    data2 := []field{{"four"}, {"five"}, {"six"}}    for _, v := range data2 {        defer v.print()    }}

This code runs up output:


With the "A brief analysis of the output of a Go language code" in the article on the idea as a basis, the above code analysis is not difficult. Yes, or according to my previous article on the "equivalent conversion" thinking to think, the method is converted to function, and then analyzed. The above code can be converted to the following code equivalent :

https://play.golang.org/p/a-vOSz4N3jbpackage mainimport (    "fmt")type field struct {    name string}func print(p *field) {    fmt.Println(p.name)}func main() {    data1 := []*field{{"one"}, {"two"}, {"three"}}    for _, v := range data1 {        defer print(v)    }    data2 := []field{{"four"}, {"five"}, {"six"}}    for _, v := range data2 {        defer print(&v)    }}

Next, we use defer's "parametric real-time evaluation" principle to analyze the above code:

Three iterations of Data1: After the defer parameter is evaluated, the defer print (v) call becomes:

    • Defer print (&field{"One"})
    • Defer print (&field{""})
    • Defer print (&field{"three"})

Data2 three iterations, after defer's parameters are evaluated, the defer print (v) call becomes:

    • Defer print (&V)
    • Defer print (&V)
    • Defer print (&V)

So before main exits, the defer function executes in the reverse order that defer is called:

    • Print (&V)
    • Print (&V)
    • Print (&V)
    • Print (&field{"three"})
    • Print (&field{"both"})
    • Print (&field{"One"})

At this point: the value stored in V is field{"Six", so the first three print outputs are "six".

Four. Summary

While defer brings some performance losses, the proper use of defer can make the logic structure of the program more concise.

"A brief analysis of the output of a Go language code" after the article issued, unexpectedly received some feedback, in fact, many go beginners want to see some of the introduction like this, but also "serious", it is best to touch the bottom of the implementation of the article. There will be a lot of focus on this later. Welcome to our site to continue to communicate, from the questions raised by friends, I can also harvest inspiration ^0^.

Famous Cloud host service vendor Digitalocean released the latest host plan, the entry-level droplet configuration upgrade to: 1 core CPU, 1G memory, 25G high-speed SSD, price 5$/month. There are friends using Digitalocean needs, you can open this link address: https://m.do.co/c/bff6eed92687 Open your do host road.

My Contact information:

Weibo: https://weibo.com/bigwhite20xx
Public Number: Iamtonybai
Blog: tonybai.com


Business cooperation: writing, writing, training, online courses, partnerships, business, consulting, advertising cooperation.

Related Article

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.