Volatil3
Volatil3

Reputation: 14978

How exactly Deferred functions are useful in Go and what alternative is available in other languages?

I know it's to deferred a process. I also read examples like it's good to use when dealing with File and other resources. My question is, is it something not being handled in other languages like Python etc? If yes how Go provides some distinctive feature?

Say there are two functions f1 and f2 and I want to call f2 when f1 is done. I can do something like:

def f1():
  done = False
  print('I am doing extensive work')
  done = True
  return done

def f2():
  print('here we go')

if f1():
  f2()

Is defer doing same thing by minimizing coding efforts?

Upvotes: 1

Views: 517

Answers (3)

Art
Art

Reputation: 20392

To understand defer, you need to understand the alternative.

In complex code dealing with many resources you end up doing things like this (pseudo code):

mtx.Lock()
file1, err := os.Open(...)
if err != nil {
    mtx.Unlock()
    return err
}
conn, err := net.Dial(...)
if err != nil {
    file1.Close()
    mtx.Unlock()
    return err
}
file2, err := os.Open(...)
if err != nil {
    conn.Close()
    file1.Close()
    mtx.Unlock()
    return err
}
do_things()
file2.Close()
conn.Close()
file1.Close()
mtx.Unlock()
return nil

This gets verbose fast. Also, it's error-prone in the long term since someone editing code and maybe moving the opening of the network connection to before opening file1 might not correctly update all the error handling paths.

In C, a common idiom here is to collect all the cleanup of resources at the end of the function and do something like this:

    ret = ERROR;
    lock(mtx);
    fd1 = open(...);
    if (fd1 == -1)
        goto err1;
    conn = net_conn(...);
    if (conn == -1)
        goto err2;
    fd2 = open(...);
    if (fd2 == -1)
        goto err3;
    do_things();

    ret = 0;
    close(fd2);
err3:
    close(conn);
err2:
    close(fd1);
err1:
    unlock(mtx);
    return ret;

This is not pretty either and it's quite easy to get this wrong too.

As a comparison doing the same thing with defer we get:

mtx.Lock()
defer mtx.Unlock()
file1, err := os.Open(...)
if err != nil {
    return err
}
defer file1.Close()
conn, err := net.Dial(...)
if err != nil {
    return err
}
defer conn.Close()
file2, err := os.Open(...)
if err != nil {
    return err
}
defer file2.Close()
.... do things ...
return nil

It's clear, concise, anyone who knows Go will understand what happens and it's very hard to get wrong or break in the future.

Upvotes: 2

icza
icza

Reputation: 417672

Go's defer statement is just another approach, it has its pros and cons (not necessarily is the best). Other languages do offer alternatives to handle proper resource closes. For example Java offers Exceptions and try-catch blocks.

Quoting from Go FAQ: Why does Go not have exceptions?

We believe that coupling exceptions to a control structure, as in the try-catch-finally idiom, results in convoluted code. It also tends to encourage programmers to label too many ordinary errors, such as failing to open a file, as exceptional.

Go takes a different approach. For plain error handling, Go's multi-value returns make it easy to report an error without overloading the return value. A canonical error type, coupled with Go's other features, makes error handling pleasant but quite different from that in other languages.

Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. The recovery mechanism is executed only as part of a function's state being torn down after an error, which is sufficient to handle catastrophe but requires no extra control structures and, when used well, can result in clean error-handling code.

See the Defer, Panic, and Recover article for details.

The advantage of using defer compared to Java's try-catch for example is that the code that handles the closing or disposing of an opened resource is right next to the code that opened it.

For example:

f, err := os.Open("file.txt")
if err != nil {
    log.Printf("Failed to open file: %v", err)
    return
}
defer f.Close()

// f is open you may read from it
...

If other languages where disposing of resources is moved to another code block, it is harder to follow where and if you actually took care of disposing it.

Upvotes: 1

omu_negru
omu_negru

Reputation: 4770

defer as the name suggests, will delay execution of the deferred function until the calling function body exits, no matter the exit point and conditions. Thus , your function f1 deffering f2 will make sure that f2 is called even if something inside f1 fails with an exception. I think of it as the finally clause from a try/catch block.

In python, the closest thing i can think of that's like defer are context managers . Notice how i say closest and not identical, since their purposes overlap a bit , but only a bit

Upvotes: 2

Related Questions