본문 바로가기
Go

[Go] Error Handling

by llHoYall 2021. 10. 22.

Error Type

error type is an interface including Error() method.

type error interface {
  Error() string
}

In other words, we can use any type that has Error() method as error type.

defer

A defer statement defers the execution of a function until the surrounding function returns.

Therefore, we can use it for error handling.

func test() {
  defer fmt.Println("Apple")
  fmt.Println("Banana")
  fmt.Println("Cherry")
}

func main() {
  test()
  // Banana
  // Cherry
  // Apple
}

A defer statement ensures that the function being called is performed even if it ends with the return keyword.

func test() int {
  defer fmt.Println("Apple")
  fmt.Println("Banana")
  return 1
  fmt.Println("Cherry")
  return 2
}

func main() {
  test()
  // Banana
  // Apple
}

So the defer keyword is usually used in code that should be executed even if an error occurs because it can guarantee that it will be called no matter what.

defer can be only used with functions or methods.

Stack Trace

Go, like other programming languages, maintains a call stack that stores a list of function calls that were activated at a specific point in time.

If a panic occurs in the program, a stack trace or call stack list is included in the panic error message.

In addition, we can use panic() function to occur a panic when we want.

panic() function can take any type of argument.

func one() {
  two()
}

func two() {
  panic("This is an error")
}

func main() {
  one()
}
// panic: This is an error
// main.two()
// main.one()
// main.main()

Even if panic occurs, all deferred function calls are executed.

func one() {
  defer fmt.Println("Defer: one()")
  two()
}

func two() {
  defer fmt.Println("Defer: two()")
  panic("This is an error")
}

func main() {
  one()
}
// Defer: two()
// Defer: one()
// panic: This is an error
// main.two()
// main.one()
// main.main()

Panic should be used in uncontrollable situations that usually indicate a bug in the program itself that is irrelevant to the user's mistakes or intentions.

recover

Go has a built-in function called recover() that can recover panicked programs.

To gracefully terminate a program that generates panic, you must use recover() function.

If recover() function is called during normal program execution, it returns only nil and does nothing.

func doRecover() {
  recover()
}

func goPanic() {
  defer doRecover()
  panic("Panicked")
  fmt.Println("After panic")
}

func main() {
  goPanic()
  fmt.Println("Exiting normally")
}
// Exiting normally

The panic-causing function is terminated immediately and all codes located behind the code that caused the panic are not executed.

However, the execution resumes after the panic has exited the function.

 

In the event of panic, recover returns all values passed to the panic.

func doRecover() {
  fmt.Println(recover())
}

func goPanic() {
  defer doRecover()
  panic("Panicked")
  fmt.Println("After panic")
}

func main() {
  goPanic()
  fmt.Println("Exiting normally")
}
// Panicked
// Exiting normally

 

A panic function has the value of interface{} type as parameters, and the return value of recover also has interface{} type.

func doRecover() {
  p := recover()
  err, ok := p.(error)
  if ok {
    fmt.Println(err.Error())
  }
}

func main() {
  defer doRecover()
  err := fmt.Errorf("This is an error")
  panic(err)
}
// This is an error

 

In the example above, if the panic value is not of error type, no value is output, so the user cannot know the reason for the failure.

If an unexpected panic occurs, we can generate a panic again to compensate for it.

func doRecover() {
  p := recover()
  err, ok := p.(error)
  if ok {
    fmt.Println(err.Error())
  } else {
    panic(p)
  }
}

func main() {
  defer doRecover()
  panic("This is a panic")
}
// panic: This is a panic [recovered]
//         panic: This is a panic

User-Defined Error

You can create your own error using errors package.

package main

import (
  "errors"
  "fmt"
)

func myError() error {
  return errors.New("Error: My Error!")
}

func main() {
  err := myError()
  fmt.Println(err)
  // Error: My Error!
}

Or you can just use Errorf() method in fmt package.

func myError() error {
  return fmt.Errorf("%v", "Error: My Error!")
}

func main() {
  err := myError()
  fmt.Println(err)
  // Error: My Error!
}

'Go' 카테고리의 다른 글

[Go] Testing  (0) 2021.10.26
[Go] goroutine and channel  (0) 2021.10.23
[Go] Map  (0) 2021.10.21
[Go] Linked List  (0) 2021.10.20
[Go] Functions  (0) 2021.10.19

댓글