Not closing transient resources
HTTP body
Mistake
type handler struct {
client http.Client
url string
}
func (h handler) getBody() (string, error) {
// resp leaks here
resp, err := h.client.Get(h.url)
if err != nil {
return "", err
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}
Fix
type handler struct {
client http.Client
url string
}
func (h handler) getBody() (string, error) {
resp, err := h.client.Get(h.url)
if err != nil {
return "", err
}
defer func(){
err := resp.Body.Close()
if err != nil {
// log
}
}()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}
sql.Rows
Mistake
// rows leaks here keeping an open connection
rows, err := db.Query("SELECT * FROM customers")
if err != nil {
return err
}
// ...
return nil
Fix
rows, err := db.Query("SELECT * FROM customers")
if err != nil {
return err
}
defer func(){
err := rows.Close()
if err != nil {
// log
}
}()
// ...
return nil
os.File
Mistake
This is fine for reading from a file, but for writing we want to capture any errors that may occur on the file close.
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
return err
}
defer func(){
err := f.Close()
if err != nil {
// log
}
}()
Fix
func writeToFile(filename string, content []byte) (err error) {
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
return err
}
defer func(){
closeError := f.Close()
if err != nil {
// if we encountered no other errors, we capture the named return
// parameter and set it equal to any closing errors
err == closeErr
}
}()
return
}