Error Handling
Examples
Filter
A panic typically means something went unexpectedly wrong. Mostly we use it to fail fast on errors that shouldn’t occur during normal operation, or that we aren’t prepared to handle gracefully.
package main
import "os"
func main() {
panic("a problem")
_, err := os.Create("/tmp/file")
if err != nil {
panic(err)
}
}
In Go it’s idiomatic to communicate errors via an explicit, separate return value. This contrasts with the exceptions used in languages like Java and Ruby and the overloaded single result / error value sometimes used in C. Go’s approach makes it easy to see which functions return errors and to h...
package main
import (
"errors"
"fmt"
)
// By convention, errors are the last return value and have the type error, a built-in interface.
func f1(arg int) (int, error) {
if arg == 42 {
// errors.New constructs a basic error value with the given error message.
return -1, errors.New("can't work with 42")
}
// A nil value in the error position indicates that there was no error.
return arg + 3, nil
}
// Custom error types implement the the "Error() string" signature.
type argError struct {
arg int
prob string
}
func (e *argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func f2(arg int) (int, error) {
if arg == 42 {
return -1, &argError{arg, "can't work with it"}
}
return arg + 3, nil
}
func main() {
for _, i := range []int{7, 42} {
if r, e := f1(i); e != nil {
fmt.Println("f1 failed:", e)
} else {
fmt.Println("f1 worked:", r)
}
}
for _, i := range []int{7, 42} {
if r, e := f2(i); e != nil {
fmt.Println("f2 failed:", e)
} else {
fmt.Println("f2 worked:", r)
}
}
_, e := f2(42)
if ae, ok := e.(*argError); ok {
fmt.Println(ae.arg)
fmt.Println(ae.prob)
}
}
struct User {
id int
name string
}
struct Repo {
users []User
}
fn new_repo() Repo {
return Repo {
users: [User{1, 'Andrew'}, User {2, 'Bob'}, User {10, 'Charles'}]
}
}
fn (r Repo) find_user_by_id(id int) ?User {
for user in r.users {
if user.id == id {
// V automatically wraps this into an option type
return user
}
}
return error('User $id not found')
}
fn main() {
repo := new_repo()
user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks
return // `or` block must end with `return`, `break`, or `continue`
}
println(user.id) // "10"
println(user.name) // "Charles"
}
// V combines Option and Result into one type, so you don't need to decide which one to use.
// The amount of work required to "upgrade" a function to an optional function is minimal: you have to add a ? to the return type and return an error when something goes wrong.
// If you don't need to return an error, you can simply return none.
// This is the primary way of handling errors in V. They are still values, like in Go, but the advantage is that errors can't be unhandled, and handling them is a lot less verbose.
// err is defined inside an or block and is set to the string message passed to the error() function. err is empty if none was returned.
user := repo.find_user_by_id(7) or {
println(err) // "User 7 not found"
return
}
// You can also propagate errors:
resp := http.get(url)?
println(resp.body)
// http.get returns ?http.Response. It was called with ?, so the error is propagated to the calling function (which must return an optional) or in case of main leads to a panic.
// Basically the code above is a shorter version of
resp := http.get(url) or {
panic(err)
}
println(resp.body)
// V does not have a way to force unwrap an optional (like Rust's unwrap() or Swift's !). You have to use or { panic(err) } instead.