DEV Community

Andi
Andi

Posted on

Go (Golang) Basic - Error

What Happens When Things Go Wrong?

We've learned to write functions, create structs, and use pointers. Our code works perfectly... in a perfect world. But in the real world, things go wrong: a file might not exist, a network connection might fail, or a user might provide invalid input.

Many languages handle these situations with "exceptions" using try-catch blocks. Go takes a fundamentally different and more explicit approach.

In Go, errors are values. An error is not a special event that stops your program; it's just another value that a function can return, like a string or an int. This philosophy forces you to consciously handle potential failures, leading to more robust and predictable code.

1. The error Type: Go's Built-in Contract

At its core, Go's error is a built-in interface. It's a very simple contract: any type that wants to be considered an error must have a single method called Error() that returns a string.

type error interface {
    Error() string
}
Enter fullscreen mode Exit fullscreen mode

You don't usually need to create your own error types from scratch, but understanding this helps you see why the system is so flexible.

2. The Idiomatic Go Pattern: if err != nil

Because errors are just values, the standard way to handle them is to check if an error was returned from a function. This leads to the most common pattern you will see in any Go codebase: if err != nil.

A function that can fail will typically return two values: (the result, an error).

Analogy: A service technician's report. When the job is done, you get two things: the fixed appliance (the result) and a report slip (the error).

  • If the job was successful, the report slip is blank (nil).
  • If something went wrong, the report slip has a description of the problem (not nil).

The Pattern in Action

// A conceptual example
result, err := someFunctionThatCanFail()
if err != nil {
    // An error occurred! Handle it here.
    // For now, we can just print it and stop.
    fmt.Println("An error happened:", err)
    return 
}

// If the code reaches this point, it means 'err' was nil.
// It is now safe to use the 'result'.
fmt.Println("Success! The result is:", result)
Enter fullscreen mode Exit fullscreen mode

3. Creating and Returning Errors

Now that we know how to check for errors, how do we create our own? Go's built-in errors package makes this very simple.

Let's build a practical example: a safe division function that returns an error if you try to divide by zero.

Code Example

package main

import (
    "errors"
    "fmt"
)

// This function returns two values: a float64 AND an error
func divide(a float64, b float64) (float64, error) {
    // If the divisor is zero, it's a predictable failure
    if b == 0 {
        // We return a zero value for the number and a new error message
        return 0, errors.New("cannot divide by zero")
    }

    // If everything is okay, we return the result and 'nil' for the error
    // 'nil' is Go's way of saying "nothing" or "no error"
    return a / b, nil
}

func main() {
    // --- Successful case ---
    result, err := divide(10.0, 2.0)
    if err != nil {
        // This block is skipped because 'err' is nil
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result 1:", result)
    }

    // --- Failure case ---
    result2, err2 := divide(10.0, 0)
    if err2 != nil {
        // This block runs because an error was returned
        fmt.Println("Error:", err2)
    } else {
        fmt.Println("Result 2:", result2)
    }
}
Enter fullscreen mode Exit fullscreen mode

This pattern of returning (result, nil) on success and (zero-value, error) on failure is fundamental to writing clean and robust Go code.

4. error vs. panic: A Crucial Distinction

Beginners often wonder when to return an error and when to use panic. The difference is a matter of expectation.

Analogy: Think of your program as a car journey.

  • error: This is like a "Check Engine" light. It's an expected problem. You can see the warning, decide to pull over, and handle it. The journey can potentially continue.
  • panic: This is like the engine suddenly exploding. This is a catastrophic, unexpected failure that indicates something is deeply wrong with the car (a bug in your code). The journey stops immediately.

Use this as your guide:

  • Use error for expected failures that are a normal part of your program's operation (e.g., invalid user input, file not found, network timeout).
  • Use panic very rarely, and only for truly exceptional situations that indicate a programmer error or a state that should be impossible to reach.

Conclusion

Go's approach to error handling is one of its most defining features. By treating errors as regular values, it encourages you to build resilient and predictable applications.

You've now learned:

  • The philosophy that "errors are values".
  • The standard if err != nil pattern for checking errors.
  • How to create and return your own errors.
  • The critical difference between a manageable error and a fatal panic.

In the final part of our foundational series, we'll learn how to structure larger projects by organizing our code into Packages. See you there!

Top comments (0)