๐Ÿšจ

if err != nil โ€” Why You'll Miss Ruby's begin/rescue

Go doesn't use exceptions. Errors are returned as values. Half your code becomes error handling.

This is the biggest shock for Ruby developers trying Go.

In Ruby, errors throw Exceptions that get caught by rescue somewhere up the stack. Write the happy path, handle errors in one rescue block.

Go is completely different. No exceptions. Errors are return values.

Ruby vs Go Error Handling

Ruby:

begin
  data = File.read("config.yml")
  config = YAML.parse(data)
  save(config)
rescue => e
  puts "Error: #{e.message}"
end

Go:

data, err := os.ReadFile("config.yml")
if err != nil { return err }
config, err := yaml.Unmarshal(data)
if err != nil { return err }
err = save(config)
if err != nil { return err }

See it? Ruby: 3 lines of happy path + 1 line rescue. Go: 3 lines of actual logic + 6 lines of error checking. if err != nil repeats after every function call.

Why This Design

Go team's philosophy: errors must not be ignored. In exception-based systems, forgetting rescue lets errors propagate silently. In Go, you receive the error and must handle it explicitly. Skip it and the compiler errors: "err declared but not used."

errors.Is, errors.As โ€” Like Ruby's is_a?

Just as Ruby catches specific exceptions with rescue ActiveRecord::RecordNotFound, Go checks error types with errors.Is(err, sql.ErrNoRows) or errors.As(err, &target).

panic/recover โ€” Seriously Don't

Go has panic(). Similar to Ruby's raise. But Go community reserves panic for situations where the program truly cannot continue. HTTP request errors get error returns, not panics.

Completely different culture from Ruby's everyday raise/rescue usage.

Ruby to Go

1

Ruby: begin/rescue/end โ†’ Go: if err != nil { return err } (after every call)

2

Ruby: raise CustomError โ†’ Go: errors.New("message") or fmt.Errorf()

3

Ruby: rescue SpecificError โ†’ Go: errors.Is(err, target) / errors.As(err, &target)

4

Ruby: raise (everyday) โ†’ Go: panic (only for fatal situations, don't use daily)

Pros

  • Can't ignore errors โ€” compiler enforces it. Prevents Ruby's "forgot rescue, exploded in production" problem
  • Error flow is explicit โ€” reading the code shows where errors occur and how they're handled

Cons

  • if err != nil takes up 40-50% of code โ€” severe visual noise
  • No Ruby-style retry pattern (rescue โ†’ retry) โ€” must implement retry logic manually

Use Cases

When converting Rails controller's rescue_from pattern to Go HTTP handler error returns