This is a creation in Article, where the information may have evolved or changed. > Learn from this section to avoid falling into the underlying defer trap this article is only suitable for beginners who want advanced learning Golang, Daniel, please take a detour. # # #1--Defer nil function if a delay function is assigned a value of ' nil ', the runtime's [' Panic '] (https://golang.org/ref/spec#Handling_panics) exception will occur at the end of the perimeter function execution and not The function of ' defer ' is called. Example ' Gofunc () {var run func () = Nil defer run () fmt. Println ("runs")} ' output result ' ' Runs️panic:runtime error:invalid memory address or nil pointer dereference ' ' # # # what happened? The function named Func runs until the end, and the ' defer ' function is executed and the ' panic ' exception is generated because the value is ' nil '. It is worth noting, however, that the declaration of ' run () ' is not a problem because it will not be called until the peripheral function is finished running. The above is just a simple case, but the same case can happen in the real world, so if you meet, you can think about falling into this hole. # # #2-use defer in loops to avoid using ' defer ' in loops, unless you know what you're doing, because the results of their execution are often unexpected. However, in some cases, it is quite convenient to use ' defer ' in a loop, for example, to transfer recursion from a function to ' defer ', but this is clearly not what this article should explain. [] (Https://raw.githubusercontent.com/studygolang/gctt-images/master/5-gotchas-defer-1/defer_inside_a_loop.png) In the example above, ' defer row '. Close () ' Delay function in the loop runs after the function ends, not after each for loop. These delay functions accumulate in the deferred call stack and can eventually cause some unpredictable problems. # # # Solution #1: Do not use ' defer ', directly at the end of the call.! [] (Https://raw.githubusercontent.com/studygolang/gctt-imagEs/master/5-gotchas-defer-1/solution_1.png) # # # Solution #2: Transfer the task to another function and then use ' defer ' in it, in which case the delay function executes at the end of each anonymous function execution. [] (Https://raw.githubusercontent.com/studygolang/gctt-images/master/5-gotchas-defer-1/solution_2.png) # # # for benchmark Testing ! [] (https://raw.githubusercontent.com/studygolang/gctt-images/master/5-gotchas-defer-1/benchmark.jpg) [View Code] ( Https://play.golang.org/p/GJ7oOMdBwJ) # # #3--Delay calling a function with a closure sometimes, for some reason, you want to delay execution of those closures. For example, connect to a database and then disconnect from the database after the query statement executes. Example "Gotype database Struct{}func (db *database) connect () (disconnect func ()) {FMT. PRINTLN ("Connect") return func () {fmt. Println ("Disconnect")}} "" Run "godb: = &database{}defer db.connect () fmt. PRINTLN ("Query db ...") ' Output result ' ' Query Db...connect ' # # # # So what's wrong? The final ' disconnect ' does not output, and finally only ' connect ', which is a bug, the final case is ' connect () ' After execution, its execution domain is saved, but the internal closure is not executed. # # # solution ' Gofunc () {db: = &database{} Close: = Db.connect () defer close () fmt. PRINTLN ("Query db ...")} "modified, ' Db.connect () ' returned a function, and then we use ' defer ' to this function to disconnect from the database after the execution of ' func () '. Output Results '"Connectquery Db...disconnect" # # # Bad way to deal with it: even though it's bad, I'd like to tell you how to solve the problem without a variable, so I want you to learn about defer or the running mechanism of the go language. "' Gofunc () {db: = &database{} defer Db.connect () () ...} The code is technically not fundamentally different from the solution above. Where the first parenthesis is the connection database (the part that is executed immediately in ' defer db.connect () '), then the second parenthesis is to defer execution of the disconnected function (that is, the closure returned) at the end of ' func () '. Attributed to ' db.connect () ' Creates a value for the closure type, and then uses ' defer ' to declare the closure function, ' db.connect () ' value needs to be evaluated so that ' defer ' knows which function to delay, which is the ' defer ' Not directly relevant but may also help you solve some problems. # # #4--using defer in an execution block you may want to execute a function that is deferred within the block after execution of the block execution, but this is not the case, they will only be executed after the execution of the function The block belongs to, which applies to all code blocks except for the function block above, such as For,switch. * * because: latency is relative to a function rather than a code block * * example ' ' gofunc main () {{defer func () {fmt. Println ("Block:defer Runs")} () Fmt. Println ("Block:ends")} FMT. The delay function of Println ("Main:ends")} ' ' Output ' ' Block:endsmain:endsblock:defer runs ' is only run at the end of the function execution, not immediately following the block in which it resides (curly braces contain Defer called), you can use curly braces to create a separate execution block, as demonstrated in the code. # # # Another solution if you want to use ' defer ' in another block, you can use an anonymous function (just like the solution we used in the second pit). "' Gofunc Main () {func () {defer func () {fmt. Println ("Func:defer Runs")} () Fmt. Println ("Func:ends") } () Fmt. Println ("Main:ends")} ' # # #5--The pit of the delay method Similarly, you can also use ' defer ' to delay the [method] (https://blog.learngoprogramming.com/ GO-FUNCTIONS-OVERVIEW-ANONYMOUS-CLOSURES-HIGHER-ORDER-DEFERRED-CONCURRENT-6799008DDE7B#61EC) is called, but it can also be a bit of a complication. # # # does not use the pointer as the receiver ' gotype car struct {model string}func (c car) Printmodel () {fmt. Println (C.model)}func main () {c: = Car{model: "DeLorean DMC-12"} Defer C.printmodel () C.model = "Chevrolet Impala"} "output knot The "DeLorean DMC-12" # # # uses a pointer object as the recipient ' Gofunc (c *car) Printmodel () {fmt. Println (C.model)} "output result" ' Chevrolet Impala ' # # # # # # # # # # # # # # # # # # Why so?! [] (Https://raw.githubusercontent.com/studygolang/gctt-images/master/5-gotchas-defer-1/what_is_going_on.png) What we need to remember is that when the peripheral function is not returned, the runtime of Go will immediately save the arguments passed to the delay function. Thus, when a method with a value as a receiver is **defer** modified, the receiver is copied at the time of declaration (in this case the *car* object), and any modifications to the copy are not visible (*car.model* in the example), because the receiver is also the input parameter. The value of the parameter (that is, "DeLorean DMC-12") is immediately derived when using the **defer** adornment. In another case, when the call is deferred, the recipient is a pointer object, and although a new pointer variable is generated, it points to the same address as the "C" pointer in the previous example. As a result, any modification will work perfectly in the same object. The above is the whole content of this article, I will add more similar pits in the following articles--hasWith at least 15 easy-to-commit defer errors on the list, if you have any ideas, please leave a comment below.
Via:https://blog.learngoprogramming.com/gotchas-of-defer-in-go-1-8d070894cb01
Author: inanc Gumus Translator: yujiahaol68 proofreading: Rxcai
This article by GCTT original compilation, go language Chinese network honor launches
This article was originally translated by GCTT and the Go Language Chinese network. Also want to join the ranks of translators, for open source to do some of their own contribution? Welcome to join Gctt!
Translation work and translations are published only for the purpose of learning and communication, translation work in accordance with the provisions of the CC-BY-NC-SA agreement, if our work has violated your interests, please contact us promptly.
Welcome to the CC-BY-NC-SA agreement, please mark and keep the original/translation link and author/translator information in the text.
The article only represents the author's knowledge and views, if there are different points of view, please line up downstairs to spit groove
3,538 reads ∙1 likes