#2— Calling defer in the wrong order

This gotcha is from 50 shades of Go, here.

Example

This code will panic when http.Get fails.

func do() error {

res, err := http.Get("http://notexists")

defer res.Body.Close()

if err != nil {

return err

} // ..code... return nil

}

Output

panic: runtime error: invalid memory address or nil pointer dereference

Why?

Because, here, we didn’t check whether the request was successful or not. Here it fails, and we call Body on a nil variable (res), hence the panic.

Solution

Always use defer after a succesful resource allocation. For this example this means: Use defer only if http.Get is succesful.

func do() error {

res, err := http.Get("http://notexists")

if res != nil {

defer res.Body.Close()

} if err != nil {

return err

} // ..code... return nil

}

With the above code, when there’s an error, the code will return the error, otherwise, it’ll close res.Body when the func returns in defer.

👉 Side-Note

Here, you also need to check whether resp is nil, this is a caveat for http.Get. Usually, when there is an error, the response will be nil and an error will be returned. But, when you get a redirection error, response will not be nil but there’ll be an error. With the above code, you’re ensuring that you’re closing the response body. You also need to discard the data received if you’re not going to use it. More details here.

Do not forget to uncomment the code line that I’ve marked in the code

UPDATE: This problem looks like doesn’t exist with http. I need to find a better example for that. So, it may still be valid for some code, but not for http. Check out this discussion: https://medium.com/@mafredri/great-article-thanks-c0e88d4df19e. Thanks Mathias Fredriksson and Patrycja Szabłowska for validating Mathias’s point. Check out it here: https://medium.com/@szablowska.patrycja/thanks-for-the-article-bdcca5eda295.