09 - Errors
📋 Jump to TakeawaysThings go wrong. A file doesn't exist. A user types "abc" when you expected a number. A config file is corrupted. In many languages, these problems crash your program with an exception. Go does it differently. Errors are just values. Functions that can fail return an error alongside their result, and you decide what to do with it.
The error Type
An error in Go is a value of type error. Functions that can fail return it as a second value:
package main
import (
"fmt"
"strconv"
)
func main() {
n, err := strconv.Atoi("42")
fmt.Println(n, err) // 42 <nil>
n, err = strconv.Atoi("abc")
fmt.Println(n, err) // 0 strconv.Atoi: parsing "abc": invalid syntax
}strconv.Atoi converts a string to an integer. If the conversion works, err is nil (Go's word for "nothing"). If it fails, err contains a message explaining what went wrong.
Notice the import block at the top. strconv is a package from Go's standard library. We'll cover imports and packages fully in the next lesson. For now, just know that you import a package to use its functions.
Checking Errors
The standard pattern is to check err immediately after the call:
package main
import (
"fmt"
"strconv"
)
func main() {
n, err := strconv.Atoi("abc")
if err != nil {
fmt.Println("That's not a number:", err)
return
}
fmt.Println("Got:", n)
}if err != nil means "if something went wrong." This pattern appears everywhere in Go.
Returning Errors from Your Functions
import "errors"
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("cannot divide by zero")
}
return a / b, nil
}Return nil when everything is fine. Return an error when something goes wrong.
Try it yourself: add package main, a func main() that calls divide, and handle the error. Run it in the Go Playground or locally.
fmt.Errorf
Include dynamic information in an error message:
func withdraw(balance, amount int) (int, error) {
if amount > balance {
return balance, fmt.Errorf("cannot withdraw %d, only %d available", amount, balance)
}
return balance - amount, nil
}Reading and Writing Files
// Read
data, err := os.ReadFile("notes.txt")
if err != nil {
fmt.Println("Could not read file:", err)
return
}
fmt.Println(string(data))
// Write
err = os.WriteFile("output.txt", []byte("Hello!"), 0644)
if err != nil {
fmt.Println("Could not write file:", err)
return
}os.ReadFile returns bytes and an error. string(data) converts bytes to text. os.WriteFile takes a filename, bytes, and a permission number (0644 = owner read/write, others read-only).
For complete running examples of file reading and writing, check the "Ready to Run" section at the bottom of this lesson.
Ignoring Errors (Don't)
You can ignore an error with _:
n, _ := strconv.Atoi("abc")
fmt.Println(n) // 0This compiles, but n is silently 0 and you have no idea something went wrong. Handle errors.
Key Takeaways
- Errors in Go are values of type
error, not exceptions - Functions that can fail return
(result, error). Checkerr != nilimmediately errors.New("message")creates a simple errorfmt.Errorf("message %s", val)creates an error with formatted textnilmeans no error. Returnnilwhen everything is fineos.ReadFileandos.WriteFileread and write files, returning errorsstrconv.Atoiconverts strings to integers, returning an error for bad input- Don't ignore errors with
_unless you truly don't care about failure