In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article will explain in detail what is the underlying principle of closures in Go. The editor thinks it is very practical, so I share it with you as a reference. I hope you can get something after reading this article.
1. What is a closure?
A function refers to external local variables. This phenomenon is called closure.
For example, in the following code, the adder function returns an anonymous function that references the local variable sum in the adder function, so the function is a closure.
Package main import "fmt" func adder () func (int) int {sum: = 0 return func (x int) int {sum + = x return sum}}
The external local variables referenced in this closure are not destroyed from the stack as the adder function returns.
We tried to call this function and found that each time we called, the value of sum was retained in the closure function for use.
Func main () {valueFunc:= adder () fmt.Println (valueFunc (2)) / / output: 2 fmt.Println (valueFunc (2)) / / output: 4} 2. Complex closure scenarios
It is easy to write a closure, but it is not enough to write a simple closure function. If you do not understand the true principle of the closure, it is easy to misjudge the execution logic of the function in some complex closure scenarios.
If nothing else, why don't you take this example?
What do you think it will print?
Is it 6 or 11?
Import "fmt" func func1 () (I int) {I = 10 defer func () {I + = 1} () return 5} func main () {closure: = func1 () fmt.Println (closure)} 3. The underlying principle of closures?
Or use the above example to analyze
Package main import "fmt" func adder () func (int) int {sum: = 0 return func (x int) int {sum + = x return sum}} func main () {valueFunc:= adder () fmt.Println (valueFunc (2)) / / output: 2}
When we analyze its escape first, it is easy to find that sum, as a local variable of adder function, is not allocated on the stack, but on the heap.
This solves the first puzzle: why doesn't sum be destroyed when the adder function returns?
$go build-gcflags= "- m-m-l" demo.go # command-line-arguments. / demo.go:8:3: adder.func1 capturing by ref: sum (addr=true assign=true width=8). / demo.go:7:9: func literal escapes to heap:. / demo.go:7:9: flow: ~ R0 = & {storage for func literal}:. / demo.go:7:9: from func literal (spill) at. / demo.go:7:9. / demo.go : 7:9: from return func literal (return) at. / demo.go:7:2. / demo.go:6:2: sum escapes to heap:. / demo.go:6:2: flow: {storage for func literal} = & sum:. / demo.go:6:2: from func literal (captured by a closure) at. / demo.go:7:9. / demo.go:6:2: from sum (reference) at. / demo.go: 8:3. / demo.go:6:2: moved to heap: sum. / demo.go:7:9: func literal escapes to heap. / demo.go:15:23: valueFunc (2) escapes to heap:. / demo.go:15:23: flow: {storage for. Argument} = & {storage for valueFunc (2)}:. / demo.go:15:23: from valueFunc (2) (spill) at. / demo.go:15:23. / demo.go:15:23: flow: {heap} = {storage for. Argument}:. / demo.go:15:23: from... Argument (spill) at. / demo.go:15:13. / demo.go:15:23: from fmt.Println (valueFunc (2)) (call parameter) at. / demo.go:15:13. / demo.go:15:13:. Argument does not escape. / demo.go:15:23: valueFunc (2) escapes to heap
But another problem arises, even if it will not be destroyed, if the value stored in the closure function is the copied value of sum, then every time the closure function is called, the sum in it should be the same, and the two calls should return 2 instead of accumulating records.
Therefore, it is safe to guess that the structure of the closure function stores the pointer of sum.
In order to verify this conjecture, we can only go to the assembly.
You can output the corresponding assembly code by executing the following command
Go build-gcflags= "- S" demo.go
The output is quite large, and I extracted the most critical line of code below, which defines the structure of the closure function.
Where F is the pointer to the function, but that's not the point. The point is that sum does store pointers, which validates our guess.
Type.noalg.struct {F uintptr; "" .sum * int} (SB), CX4. Puzzle revealed
With the background knowledge of the third section above, you must have the answer to the question given in the second section.
First, because I is declared on the return value of the function definition, according to go's caller-save pattern, the I variable is stored in the stack space of the main function.
Then, func1's return re-assigns 5 to I, where I = 5
Because the closure function stores the pointer to this variable I.
So finally, the self-increment of I in defer is directly updated to the pointer of I, where I = 5 times 1, so the final printed result is 6.
Import "fmt" func func1 () (I int) {I = 10 defer func () {I + = 1} () return 5} func main () {closure: = func1 () fmt.Println (closure)} 5. Change the subject again
If you understand the above question, let's take a look at the following one.
For the return value of func1, we don't write the variable name I, and then we used to return the specific literal amount, but now it is changed to variable I, which is these two small changes, which will lead to a big difference in the running result. You can think about the result.
Import "fmt" func func1 () (int) {I: = 10 defer func () {I + = 1} () return I} func main () {closure: = func1 () fmt.Println (closure)}
If you write the variable name in the return value, then the variable will be stored in the stack space of main, and if you don't write, then I can only be stored in the stack space of func1, at the same time, the value of return will not act on the original variable I, but will be stored in the function in another stack memory.
So if you increment the original I in defer, it will not affect the return value of func1.
So the printed result can only be 10.
Did you get it right?
This is the end of this article on "what is the underlying principle of closures in Go". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.