This article states: "A defer statement pushes a function call onto a list." I'm wondering if I can access the elements in that list from another place in my program and then invoke them? Can I invoke them multiple times? I'm assuming that I have a reference to the function that has defer behavior (if that helps).
So, here's a short example of what I want to do:
func main {
doStuff = func() {
// open database connections
// write temporary files
// etc...
defer func() {
// close database connections
// delete temporary files
// etc...
}()
}
AwesomeApplication(doStuff)
}
func AwesomeApplication(doStuff func()) {
// Now, can I get a reference to the defer function within `doStuff`?
// No, I can't just define the defer function somewhere an pass it
// with `doStuff`. Think of this as a curiosity I want to satisfy,
// not a real use case.
}
In Go language, defer statements delay the execution of the function or method or an anonymous method until the nearby functions returns. In other words, defer function or method call arguments evaluate instantly, but they don't execute until the nearby functions returns.
defer statement is a convenient way to execute a piece of code before a function returns, as explained in Golang specification: Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred.
defer is often used where e.g. ensure and finally would be used in other languages. Suppose we wanted to create a file, write to it, and then close when we're done.
In Go, we use defer, panic and recover statements to handle errors. We use defer to delay the execution of functions that might cause an error. The panic statement terminates the program immediately and recover is used to recover the message during panic.
The 'list' where to defer
calls are stored are fully implementation specific so you have no reliable way of getting to this list.1,2 The implementation details for the *g compiler family (albeit a bit older) can be found in Russ Cox' research blog.
Deferred functions are associated with the current goroutine (g->Defer
) and
(in case of *g family) identified by the current stack pointer. If the current stack frame matches
the stack frame stored in the topmost Defer
entry, this function is called.
With this knowledge it is possible to access the list of deferred functions using cgo. You need to know
However, I don't recommend using this. A general solution for the use case you're describing would be to have a function like this:
func setupRoutines() (setUp, tearDown func()) {
// store db connection object and such
return func() { /* connect db and such */ }, func() { /* close db and such */ }
}
In your code you could then share the tearDown
function, which would be called using defer
.
This way you still have the bonus of having all your database connections and such local but you're
able to share the initialization/disconnect functions.
If you're really interested in playing around with unsafe
and C, you can use the following code as a template.
inspect/runtime.c:
// +build gc
#include <runtime.h>
void ·FirstDeferred(void* foo) {
foo = g->defer->fn;
FLUSH(&foo);
}
inspect/inspect.go
package inspect
import "unsafe"
func FirstDeferred() unsafe.Pointer
defer.go
package main
import "defer/inspect"
func f(a, b int) {
println("deferred f(", a, b, ")")
}
func main() {
defer f(1, 2)
println( inspect.FirstDeferred() )
}
This code (based on this) gives you access to the current go routine (g
) and therefore
the defer
attribute of it. Therefore you should be able to access the pointer to the function
and wrap it in a go FuncVal
and return it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With