Golang errors: Difference between revisions
From wikinotes
(→Errors) |
|||
Line 4: | Line 4: | ||
= Errors = | = Errors = | ||
<blockquote> | <blockquote> | ||
== Example == | |||
<blockquote> | |||
<syntaxhighlight lang="go"> | |||
package main | |||
import "errors" | |||
import "fmt" | |||
import "os" | |||
var ErrDivisionByZero = errors.New("Cannot divide by 0") | |||
var ErrOverNineThousand = errors.New("Cannot sum to over 9000") | |||
func Divide(a int, b int) (result int, err error) { | |||
if b == 0 { | |||
return 0, ErrDivisionByZero | |||
} | |||
return a / b, nil | |||
} | |||
func DivideThenAdd(a int, b int, c int) (result int, err error) { | |||
res, err := Divide(a, b) | |||
if err != nil { | |||
return 0, fmt.Errorf("DivideThenAdd: %w", err) | |||
} | |||
res += c | |||
if res > 9000 { | |||
return 0, ErrOverNineThousand | |||
} | |||
return res, nil | |||
} | |||
func main() { | |||
var err error | |||
res, err := DivideThenAdd(10, 0, 1) | |||
if err != nil { | |||
if errors.Is(err, ErrDivisionByZero) { | |||
fmt.Println("Cannot divide by zero!!") | |||
if errors.Is(err, ErrOverNineThousand) { | |||
} else { | |||
fmt.Println("I'd prefer we kept numbers below 9000") | |||
} | |||
os.Exit(1) | |||
} | |||
fmt.Println(res) | |||
} | |||
</syntaxhighlight> | |||
</blockquote><!-- Example --> | |||
== Returning Errors == | == Returning Errors == | ||
<blockquote> | <blockquote> |
Revision as of 14:15, 18 June 2022
Go seems to discourage the use of exception-style control-flows,
encouraging the use of errors in return-values instead.
Errors
Example
package main import "errors" import "fmt" import "os" var ErrDivisionByZero = errors.New("Cannot divide by 0") var ErrOverNineThousand = errors.New("Cannot sum to over 9000") func Divide(a int, b int) (result int, err error) { if b == 0 { return 0, ErrDivisionByZero } return a / b, nil } func DivideThenAdd(a int, b int, c int) (result int, err error) { res, err := Divide(a, b) if err != nil { return 0, fmt.Errorf("DivideThenAdd: %w", err) } res += c if res > 9000 { return 0, ErrOverNineThousand } return res, nil } func main() { var err error res, err := DivideThenAdd(10, 0, 1) if err != nil { if errors.Is(err, ErrDivisionByZero) { fmt.Println("Cannot divide by zero!!") if errors.Is(err, ErrOverNineThousand) { } else { fmt.Println("I'd prefer we kept numbers below 9000") } os.Exit(1) } fmt.Println(res) }Returning Errors
Go prefers passing error objects as return values to panicking (go's exception-like behaviour).
Errors can be any object that expose theError() string
method.require "errors" require "fmt" // build ad-hoc error fmt.Errorf("User does not exist") errors.New("User does not exist")Functions generally return an error as the last value on fail,
or nil on successfunc doThing (value int, error) { if success { return 123, nil // success } return 0, fmt.Errorf("User does not exist") // fail }Wrapping Errors
Handling Errors
func doThing { var err error if err = doThingOne; err != nil { if errors.Is(err, SomePreDefinedError) {...} if errors.As(err, SomeWrappedError) {...} } }Error Types
Errors are often pre-defined as package constants
require "errors" var UserNotExistError = errors.New("User does not exist") func DoAs(user string) error { return UserNotExistError }You can then check for a specific error
require "errors" if err := DoAs("someuser"); err != nil { if errors.Is(err) {...} }
Panic
panic
A
panic
is go's repacement for exceptions.
If a panic is not caught, it bubbles to the top of the application, and it exits with an error.panic("I encountered an error")recover
You can check the value of a panic (if one has been raised) using
recover()
.panic("I encountered an error") // raise a panic err := recover() // returns nil/error-msg-if-presentIt is common to handle panics in deferred functions
func main() { fmt.Println("hi") panic("I just encountered an error") defer func() { if err := recover(); err != nil { fmt.Println("Error: ", err) panic(err) // <<-- re-raise panic } } fmt.Println("bye") // <-- never runs }