views:

172

answers:

6

I am not familiar with mechanisms similar to Go's "defer" in other languages. Are there similar mechanisms in other languages?

+3  A: 

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 way to deal with situations such as resources that must be released regardless of which path a function takes to return. The canonical examples are unlocking a mutex or closing a file.

In Java, you would use a finally block for that.

Deferring a function like this has two advantages. First, it guarantees that you will never forget to close the file, a mistake that's easy to make if you later edit the function to add a new return path.

Same with finally.

Second, it means that the close sits near the open, which is much clearer than placing it at the end of the function.

That part is different. In Java, you'd have to place the code at the end. The Go defer statement allows you to declare the clean-up code "inline" instead, which is an interesting twist.

Also, I am assuming that you can use defer within loops and conditional statements, which would give you kind of a dynamically constructed finally block.

Thilo
Correct, you can use defer anywhere within a function and any number of times (memory allowing), though I don't believe there's any thing in the spec about the order in which they run (though the current implementation is FILO)
cthom06
likewise you use finally in c#
Ian Ringrose
+6  A: 

D has a similar construct, the 'scope guard statement', used as

scope (exit) { /* do stuff */ }
scope (success) { }
scope (failure) { }

which run the accompanying block upon any exit, exit without exception and exit with exception respectively, and are equivalent to a try/finally block without the excessive nesting.

Scott Wales
+1 for pointing out the excessive nesting with try/catch. Also scope seems to be even more flexible than defer, in that it can be used with any scope (not just for the whole function).
Thilo
+1  A: 

Maybe destructors in C++. They are called when you are going out from any context(especially function context). So, if you want to "defer" something put it in destructor of some object.

+2  A: 

I have used a similar mechanism for C++ in the past, for freeing resources of a C library had to use in my project. The class is:

/** A simple class whose job is to mainly help resource cleanup.
 *
 * The registered callback is called when the current scope is left.
 */
class CallAfterScope
{
public:
    /** Constructor.*/
    CallAfterScope(const boost::function<void()>& callback)
        : m_callback(callback)
    {}

    /** Destructor: If there is an assigned callback, call it. */
    ~CallAfterScope()
    {
        m_callback();
    }    
private:    
    boost::function<void()> m_callback;
};

The way I used it was (this is a function that makes use of OpenSSL, if memory serves me):

ErrCode Crypto::GenRsaKeyPair(PrivateKey::SmartPtr pPrivateKey,
                              PublicKey::SmartPtr pPublicKey,
                              size_t keyNumBits,
                              uint32_t pubExp)
{
    RSA* pRsa = RSA_new();
    if (pRsa == 0)
    {
        return Err::no_resources;
    }
    CallAfterScope freeRsa(boost::bind(RSA_free, pRsa));

    ...
}

The advantage of using this cleanup mechanism is that the the number of lines of code required to perform cleanup drops down to 1 - constructing the cleanup object, and bringing the cleanup scheduling code near the allocation code.

By the way, this was only intended for resources I would allocate and release during a single function call. I implemented a proper wrapper class for anything that I needed to keep around and operate on for a longer duration (such as the PrivateKey and PublicKey classes that I pass to this function). The idea was to minimize the amount of wrapper classes I would need to implement.

Dysaster
+1  A: 

TL;DR: In a way, defer is a very, very old ASM trick finally being put to use in a higher level language.

Not a useful answer in any way, but possibly interesting: it just so happens I posted an anecdote on the Go mailing list today on how you can do this in Z80 ASM:

In Z80, you run a procedure with CALL label. Procedures end with RET. I'll quote from Sean McLaughlin's excellent tutorial:

"To understand CALL fully, you need to be aware of a specialized register called the program counter PC. The program counter holds the address of the currently executing instruction. What happens during CALL is that the current program counter value is pushed onto the stack, then a transfer to the label is done. RET pops the top stack value into the program counter."

.. so what I figured was that there was nothing stopping you from PUSH-ing a label for another procedure on the stack (or more of them, if you want). If you'd then call RET you'd jump to the other procedure first, which would of course end with a RET itself, finishing everything properly.

I never found a use for it though, the needs of TI-83+ programs aren't that complicated :P. The more standard control structures were always more appropriate for my needs.

Turns out this is fairly similar to how Go's defer works under the hood. Cox even mentions the following:

"All three are also the kinds of tricks that were commonplace in the early days of Unix, since it had been written in assembly. For example, the original fork system call handler distinguished parent from child by changing the return address just as jmpdefer does. In modern Unix, the fork system call returns the new process id in the parent but returns zero in the child. In the early versions, including Sixth Edition, fork returns the new process id in both, but the child returned normally while the parent returned to one instruction past the usual return address. Thus the instruction after invoking the fork system call needed to be an unconditional jump to the child-specific code."

Job van der Zwan
Tricks that do not match calls and returns are most likely counterproductive as they cause the branch prediction's return address stack to go out of sync. This means that all the returns in that stack (e.g. 16 of them) will be mispredicted.Tweaking a return address is less bad; it causes one misprediction only.
jilles
In general, sure, but Z80 has one shared stack for everything, so that issue doesn't quite apply (the only requirement is that you haven't PUSHed anything else on the stack since calling the procedure). The thing with this trick is that AFAIK it always is less efficient.
Job van der Zwan
Well, I can think of one hypothetical scenario, if you need to run sequences of procedures which are long, non-trivial and known beforehand, but that involves messing with the stack pointer and using self-modifying code to make everything still function in the end. If you find yourself in that scenario, let alone feel the need to complicate your code that much just to "save" the use of a CALL every time you need to run a procedure, I really suggest you take another look at what the hell you think you're doing ;).
Job van der Zwan
A: 

Using the same system as @Dysaster I implemented what I called a DelayedAction mechanism in Python, and used it mainly for DelayedLog.

The idea is that in Python when an exception is thrown, you have the callstack, but you don't have the actual arguments (their values), thus I guarded a couple of critical functions in the following manner:

def mayFail(arg0, arg1, arg2):
  delayed = DelayedLog('mayFail - ' + str(arg0) + str(arg1) + str(arg2))

  // do stuff

  delayed.cancel()

At worse, I forget to cancel before an early return and do log, but it's usually easy to clean-up.

Matthieu M.