When to use `defer`? [solved]

I (think) I understand how defer works and I see how it can be useful. But how do I know when to use defer?

Based on an online example I made this:

newFile, err := os.Create("new-file.txt")
if err != nil {
    log.Fatal(err)
}
defer newFile.Close()

Here I used defer and it worked fine. But when I lookup os.Create() there’s no mention of the need for defer. It also isn’t discussed on the os package page.

My confusion gets worse when I Google on when to use defer. Here are some anonymous statements I came across (to not discredit those who said them):

if the function is even slightly more complex, I do feel that defer is worth using

We use defer to close or deallocate resources usually.

I appreciate all the information that’s available online about defer, but as a newcomer it’s not clear to me when I should call defer.

In case of the documentation for os.Create(), there’s no mention it leaves resources open which I’d need to close with defer. If it would say that os.Create() leaves open resources, I’d in this instance know I’ll need to close those myself. (And thus use defer.)

In general when you open a resource like a file or database connection, the operating system reserves a file descriptor for your program. File descriptors are limited so when you are done with it you need to close it to signal the operating system to release the resource.

The function os.Create returns an *os.File and if you look at its supported methods you can see Close so that is a good hint.

1 Like

Beyond closing resources, there are other interesting uses of defer such as releasing mutexes:

mu.Lock()
defer mu.Unlock()

If you haven’t already, you can read more about defer in Defer, Panic, and Recover.

It’s important to understand that defer func will be executed even in case of panic inside to function body where defer belongs. Another important thing is that defer will take values of the variables at the moment where the defer is, all later changes will not apply.

https://blog.learngoprogramming.com/gotchas-of-defer-in-go-1-8d070894cb01

https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa

https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-iii-36a1ab3d6ef1

To summarize, use defer when you want to ensure to clean up resources whenever a function exists, no matter how it exits.

As an example, consider a function that has three return directives and calls functions that may panic. Without defer you would have to call the cleanup code at three locations in your function. And none of the three cleanups would be called in case of a panic. And later, when you add a fourth return, you’d have to remember to add the cleanup code also there, to prevent a resource leak.

Using a defer directive helps avoiding these problems.

1 Like

Thanks for all replies! Much appreciated. :slight_smile:

Ah I see, thanks. Because os.Create() returns a pointer to File and since the File struct has a Close() function, I’d better use defer to close the value that os.Create() returns.

I figure that the same goes for the Conn and DB structs from the sql package, because those types also have a Close() function associated with them?

And likewise if I use the ReadFull() function from io I do not need to use defer, since that function returns a Reader and that type doesn’t have a Close() function. Right?

I understand this and all the motivation for using defer. But it’s not clear to me when there’s a potential for a resource leak.

Would the presence of a Close() function be sufficient information I need to clean up resources? (I exclude mutex from this, because when I create a mutex I’ll also need to close it myself. But for the os.Create() function it wasn’t clear to me I was opening resources that would remain open.)

Yes that is right. A good example is ioutil.ReadFile. If you look at the code of ReadFile you can see that it opens the file for you and handles closing. That is because its purpose is to read the whole file at once.

In the other case, you open a file yourself and you keep it open in order to perform an arbitrary number of operations on it. So only you can know when these operations are done that’s why you need to close the file yourself. I agree that os.Create can be a bit tricky because of the name. But if you look at the code of os.Create, you can see that all it does it call os.OpenFile which makes things clearer. :slight_smile:

1 Like

Thanks Nstratos for your easy-to-follow explanations. It’s totally clear to me now. :slight_smile:

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.