try

package module
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 28, 2021 License: Unlicense Imports: 2 Imported by: 0

README

Overview

Shorter error handling for Go. Supports two approaches:

Features:

  • Uses a combination of defer and panics to make code significantly shorter, at an acceptable runtime cost.
  • Automatically ensures stacktraces via "github.com/pkg/errors".
  • You can choose to keep error in signatures and use explicit "try".
  • You can choose to drop error from signatures and use exceptions.

See API docs at https://pkg.go.dev/github.com/mitranim/try.

TOC

Why

Go wants you to add meaningful context when handling errors. I sympathize with this idea, and do it often. But there's code where annotating every single failure is not practical and/or bloats the code beyond our ability to read it back.

func someFuncA() error {
  err := someFuncB()
  if err != nil {
    return errors.WithMessage(err, `failed to X`)
  }
  err = someFuncC()
  if err != nil {
    return errors.WithMessage(err, `failed to X`)
  }
  err = someFuncD()
  if err != nil {
    return errors.WithMessage(err, `failed to X`)
  }
  return nil
}

Using the "try" style:

func someFuncA() (err error) {
  defer try.RecWithMessage(&err, `failed to X`)
  try.To(someFuncB())
  try.To(someFuncC())
  try.To(someFuncD())
  return
}

Using the "exceptions" style:

func someFuncA() {
  defer try.Detail(`failed to X`)
  someFuncB()
  someFuncC()
  someFuncD()
}

The code should speak for itself. This won't be usable for every codebase, see Limitations below, but can be a nice improvement for some.

Limitations

This has a minor runtime cost. Use this for IO-heavy control code, where the cost is barely measurable. Avoid this in CPU-heavy code or libraries.

This package provides a variety of "try" functions for common cases, but it can't define something generic like the original proposal did. To make your code compatible, prefer to use pointers for "inout" parameters of non-primitive types, and return only error:

func someFunc(input A, out *B) error {
  *out = someOperation(input)
  return someErr
}

var val B
try.To(someFunc(input, &val))

...Or use inout parameters and panics:

func someFunc(input A, out *B) {
  *out = someOperation(input)
}

var val B
someFunc(input, &val)

In the current state of Go, functions conforming to this pattern are easier to compose, leading to much shorter code.

Naming

The term "must" is more conventional in the Go standard library, but this library uses "try" because it's more grammatically flexible: "try string" works, but "must string" would not. The "try" proposal used "try". Swift error handling is very similar and uses "try". (Unlike Swift, we have stacktraces.)

Changelog

v0.1.5

Breaking renaming for consistency:

  • IgnoreIgnoreOnly
  • IgnoringIgnoringOnly
  • WithTransTransing

Added:

  • Ignore
  • Ignoring

v0.1.4

Breaking: renamed Caught to CaughtOnly for consistency, added Caught.

v0.1.3

Added DetailOnly and DetailOnlyf.

v0.1.2

Added tools to support the "exceptions" style. For many apps, it's a better fit than either the Go style or the "try" style.

License

https://unlicense.org

Misc

I'm receptive to suggestions. If this library almost satisfies you but needs changes, open an issue or chat me up. Contacts: https://mitranim.com/#contacts

Documentation

Overview

Shorter error handling in Go. Supports two styles:

* Like the rejected "try" proposal (https://golang.org/design/32437-try-builtin).

* Exception-based.

Uses a combination of `defer` and panics to make code SIGNIFICANTLY shorter, at an acceptable runtime cost. Automatically ensures stacktraces via "github.com/pkg/errors". You can choose to keep `error` in signatures and use explicit "try", or drop `error` from signatures and use exceptions.

See `readme.md` and examples.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bool

func Bool(val bool, err error) bool

A "try" function that takes and returns a value of type `bool`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (bool, error) { return true, nil }
	fmt.Println(try.Bool(someFunc()))
}
Output:

true

func BoolSlice

func BoolSlice(val []bool, err error) []bool

A "try" function that takes and returns a value of type `[]bool`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]bool, error) { return []bool{true}, nil }
	fmt.Println(try.BoolSlice(someFunc()))
}
Output:

[true]

func Byte

func Byte(val byte, err error) byte

A "try" function that takes and returns a value of type `byte`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (byte, error) { return 255, nil }
	fmt.Println(try.Byte(someFunc()))
}
Output:

255

func ByteSlice

func ByteSlice(val []byte, err error) []byte

A "try" function that takes and returns a value of type `[]byte`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]byte, error) { return []byte{255}, nil }
	fmt.Println(try.ByteSlice(someFunc()))
}
Output:

[255]

func Catch added in v0.1.2

func Catch(fun func()) (err error)

Converts a panic to an error, idempotently adding a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	someFunc := func() {
		try.To(errors.New(`failure A`)) // Will panic.
		try.To(errors.New(`failure B`)) // Not executed; would panic.
		try.To(nil)                     // Not executed; would not panic.
	}
	err := try.Catch(someFunc)
	fmt.Println(err)
}
Output:

failure A

func CatchOnly added in v0.1.2

func CatchOnly(test func(error) bool, fun func()) (err error)

Converts a panic to an error, if the error satisfies the provided test. Otherwise re-panics. Idempotently adds a stacktrace.

Example
package main

import (
	"fmt"
	"os"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	isErrNoFile := func(err error) bool {
		return errors.Is(err, os.ErrNotExist)
	}

	maybeRead := func() {
		fmt.Println(try.ByteSlice(os.ReadFile(`non-existent-file`)))
	}

	err := try.CatchOnly(isErrNoFile, maybeRead)
	fmt.Println(err)
}
Output:

open non-existent-file: no such file or directory

func Caught added in v0.1.2

func Caught(fun func()) bool

Shortcut for `Catch() != nil`. Useful when you want to handle all errors while ignoring their content.

Example
package main

import (
	"fmt"
	"os"

	"github.com/mitranim/try"
)

func main() {
	maybeRead := func() {
		fmt.Println(try.ByteSlice(os.ReadFile(`non-existent-file`)))
	}

	fmt.Println(try.Caught(maybeRead))
}
Output:

true

func CaughtOnly added in v0.1.4

func CaughtOnly(test func(error) bool, fun func()) bool

Shortcut for `CatchOnly() != nil`. Useful when you want to handle a specific error while ignoring its content.

Example
package main

import (
	"fmt"
	"os"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	isErrNoFile := func(err error) bool {
		return errors.Is(err, os.ErrNotExist)
	}

	maybeRead := func() {
		fmt.Println(try.ByteSlice(os.ReadFile(`non-existent-file`)))
	}

	fmt.Println(try.CaughtOnly(isErrNoFile, maybeRead))
}
Output:

true

func Complex128

func Complex128(val complex128, err error) complex128

A "try" function that takes and returns a value of type `complex128`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (complex128, error) { return 255, nil }
	fmt.Println(try.Complex128(someFunc()))
}
Output:

(255+0i)

func Complex128Slice

func Complex128Slice(val []complex128, err error) []complex128

A "try" function that takes and returns a value of type `[]complex128`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]complex128, error) { return []complex128{255}, nil }
	fmt.Println(try.Complex128Slice(someFunc()))
}
Output:

[(255+0i)]

func Complex64

func Complex64(val complex64, err error) complex64

A "try" function that takes and returns a value of type `complex64`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (complex64, error) { return 255, nil }
	fmt.Println(try.Complex64(someFunc()))
}
Output:

(255+0i)

func Complex64Slice

func Complex64Slice(val []complex64, err error) []complex64

A "try" function that takes and returns a value of type `[]complex64`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]complex64, error) { return []complex64{255}, nil }
	fmt.Println(try.Complex64Slice(someFunc()))
}
Output:

[(255+0i)]

func Detail added in v0.1.2

func Detail(msg string)

Must be deferred. Wraps non-nil panics, prepending the error message and idempotently adding a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	someFunc := func() {
		defer try.Detail(`failed to X`)
		try.To(errors.New(`failure A`)) // Will be wrapped.
		try.To(errors.New(`failure B`)) // Not executed; would panic.
		try.To(nil)                     // Not executed; would not panic.
	}
	err := try.Catch(someFunc)
	fmt.Println(err)
}
Output:

failed to X: failure A

func DetailOnly added in v0.1.3

func DetailOnly(test func(error) bool, msg string)

Must be deferred. Wraps non-nil panics, prepending the error message, ONLY if they satisfy the provided test. Idempotently adds a stacktrace to all panics.

Example
package main

import (
	"fmt"
	"os"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	isErrNoFile := func(err error) bool {
		return errors.Is(err, os.ErrNotExist)
	}

	someFunc := func() {
		defer try.DetailOnly(isErrNoFile, `file not found`)
		_ = try.ByteSlice(os.ReadFile(`non-existent-file`))
	}

	err := try.Catch(someFunc)
	fmt.Println(err)
}
Output:

file not found: open non-existent-file: no such file or directory

func DetailOnlyf added in v0.1.3

func DetailOnlyf(test func(error) bool, msg string, args ...interface{})

Must be deferred. Wraps non-nil panics, prepending the error message, ONLY if they satisfy the provided test. Idempotently adds a stacktrace to all panics.

Example
package main

import (
	"fmt"
	"os"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	isErrNoFile := func(err error) bool {
		return errors.Is(err, os.ErrNotExist)
	}

	someFunc := func() {
		const name = `non-existent-file`
		defer try.DetailOnlyf(isErrNoFile, `file %q not found`, name)
		_ = try.ByteSlice(os.ReadFile(name))
	}

	err := try.Catch(someFunc)
	fmt.Println(err)
}
Output:

file "non-existent-file" not found: open non-existent-file: no such file or directory

func Detailf added in v0.1.2

func Detailf(msg string, args ...interface{})

Must be deferred. Wraps non-nil panics, prepending the error message and idempotently adding a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	someFunc := func() {
		defer try.Detailf(`failed to %v`, `X`)
		try.To(errors.New(`failure A`)) // Will be wrapped.
		try.To(errors.New(`failure B`)) // Not executed; would panic.
		try.To(nil)                     // Not executed; would not panic.
	}
	err := try.Catch(someFunc)
	fmt.Println(err)
}
Output:

failed to X: failure A

func Err

func Err(val interface{}) error

Converts an arbitrary value to an error. Should be used with `recover()`:

err := Err(recover())

Caution: `recover()` works ONLY when called DIRECTLY inside a deferred function. When called ANYWHERE ELSE, even in functions called BY a deferred function, it's a nop.

When called with `nil`, returns `nil`. When called with a non-nil non-error value, wraps it into `Val` which implements the `error` interface.

func Fail added in v0.1.2

func Fail(fun func(error))

Must be deferred. Runs the function ONLY if there's an ongoing panic, and then re-panics. Idempotently adds a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	logErr := func(err error) { fmt.Println(`caught:`, err) }

	funcOk := func() {
		defer try.Fail(logErr)
		fmt.Println(`not panicking`)
	}

	funcFail := func() {
		defer try.Fail(logErr)
		fmt.Println(`panicking`)
		panic(errors.New(`failure`))
	}

	funcOk()
	_ = try.Catch(funcFail)
}
Output:

not panicking
panicking
caught: failure

func Float32

func Float32(val float32, err error) float32

A "try" function that takes and returns a value of type `float32`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (float32, error) { return 255, nil }
	fmt.Println(try.Float32(someFunc()))
}
Output:

255

func Float32Slice

func Float32Slice(val []float32, err error) []float32

A "try" function that takes and returns a value of type `[]float32`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]float32, error) { return []float32{255}, nil }
	fmt.Println(try.Float32Slice(someFunc()))
}
Output:

[255]

func Float64

func Float64(val float64, err error) float64

A "try" function that takes and returns a value of type `float64`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (float64, error) { return 255, nil }
	fmt.Println(try.Float64(someFunc()))
}
Output:

255

func Float64Slice

func Float64Slice(val []float64, err error) []float64

A "try" function that takes and returns a value of type `[]float64`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]float64, error) { return []float64{255}, nil }
	fmt.Println(try.Float64Slice(someFunc()))
}
Output:

[255]

func HasStack

func HasStack(err error) bool

True if this error, or any of the errors it wraps, has a stacktrace provided by "github.com/pkg/errors".

func Ignore added in v0.1.2

func Ignore()

Must be deferred. Catches and ignores ALL panics.

Example
package main

import (
	"fmt"
	"os"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() {
		defer try.Ignore()
		_ = try.ByteSlice(os.ReadFile(`non-existent-file`))
		fmt.Println(`file exists`)
	}

	someFunc()
}
Output:

func IgnoreOnly added in v0.1.5

func IgnoreOnly(test func(error) bool)

Must be deferred. Catches panics; ignores errors that satisfy the provided test; re-panics on other non-nil errors. Idempotently adds a stacktrace.

Example
package main

import (
	"fmt"
	"os"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	isErrNoFile := func(err error) bool {
		return errors.Is(err, os.ErrNotExist)
	}

	someFunc := func() {
		defer try.IgnoreOnly(isErrNoFile)
		_ = try.ByteSlice(os.ReadFile(`non-existent-file`))
		fmt.Println(`file exists`)
	}

	someFunc()
}
Output:

func Ignoring added in v0.1.2

func Ignoring(fun func())

Runs a function, catching and ignoring ALL panics.

Example
package main

import (
	"fmt"
	"os"

	"github.com/mitranim/try"
)

func main() {
	maybeRead := func() {
		_ = try.ByteSlice(os.ReadFile(`non-existent-file`))
		fmt.Println(`file exists`)
	}

	try.Ignoring(maybeRead)
}
Output:

func IgnoringOnly added in v0.1.5

func IgnoringOnly(test func(error) bool, fun func())

Runs a function, catching and ignoring only the panics that satisfy the provided test. Idempotently adds a stacktrace to all panics.

Example
package main

import (
	"fmt"
	"os"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	isErrNoFile := func(err error) bool {
		return errors.Is(err, os.ErrNotExist)
	}

	maybeRead := func() {
		_ = try.ByteSlice(os.ReadFile(`non-existent-file`))
		fmt.Println(`file exists`)
	}

	try.IgnoringOnly(isErrNoFile, maybeRead)
}
Output:

func Int

func Int(val int, err error) int

A "try" function that takes and returns a value of type `int`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (int, error) { return 255, nil }
	fmt.Println(try.Int(someFunc()))
}
Output:

255

func Int16

func Int16(val int16, err error) int16

A "try" function that takes and returns a value of type `int16`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (int16, error) { return 255, nil }
	fmt.Println(try.Int16(someFunc()))
}
Output:

255

func Int16Slice

func Int16Slice(val []int16, err error) []int16

A "try" function that takes and returns a value of type `[]int16`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]int16, error) { return []int16{255}, nil }
	fmt.Println(try.Int16Slice(someFunc()))
}
Output:

[255]

func Int32

func Int32(val int32, err error) int32

A "try" function that takes and returns a value of type `int32`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (int32, error) { return 255, nil }
	fmt.Println(try.Int32(someFunc()))
}
Output:

255

func Int32Slice

func Int32Slice(val []int32, err error) []int32

A "try" function that takes and returns a value of type `[]int32`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]int32, error) { return []int32{255}, nil }
	fmt.Println(try.Int32Slice(someFunc()))
}
Output:

[255]

func Int64

func Int64(val int64, err error) int64

A "try" function that takes and returns a value of type `int64`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (int64, error) { return 255, nil }
	fmt.Println(try.Int64(someFunc()))
}
Output:

255

func Int64Slice

func Int64Slice(val []int64, err error) []int64

A "try" function that takes and returns a value of type `[]int64`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]int64, error) { return []int64{255}, nil }
	fmt.Println(try.Int64Slice(someFunc()))
}
Output:

[255]

func Int8

func Int8(val int8, err error) int8

A "try" function that takes and returns a value of type `int8`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (int8, error) { return -127, nil }
	fmt.Println(try.Int8(someFunc()))
}
Output:

-127

func Int8Slice

func Int8Slice(val []int8, err error) []int8

A "try" function that takes and returns a value of type `[]int8`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]int8, error) { return []int8{-127}, nil }
	fmt.Println(try.Int8Slice(someFunc()))
}
Output:

[-127]

func IntSlice

func IntSlice(val []int, err error) []int

A "try" function that takes and returns a value of type `[]int`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]int, error) { return []int{255}, nil }
	fmt.Println(try.IntSlice(someFunc()))
}
Output:

[255]

func Interface

func Interface(val interface{}, err error) interface{}

A "try" function that takes and returns a value of type `interface{} value.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (interface{}, error) { return "val", nil }
	fmt.Println(try.Interface(someFunc()))
}
Output:

val

func InterfaceSlice

func InterfaceSlice(val []interface{}, err error) []interface{}

A "try" function that takes and returns a value of type `[]interface{}`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]interface{}, error) { return []interface{}{"val"}, nil }
	fmt.Println(try.InterfaceSlice(someFunc()))
}
Output:

[val]

func Ok added in v0.1.2

func Ok(fun func())

Must be deferred. Runs the function only if there's no panic. Idempotently adds a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	logOk := func() { fmt.Println(`no panic`) }

	funcOk := func() {
		defer try.Ok(logOk)
		fmt.Println(`not panicking`)
	}

	funcFail := func() {
		defer try.Ok(logOk)
		fmt.Println(`panicking`)
		panic(errors.New(`failure`))
	}

	funcOk()
	_ = try.Catch(funcFail)
}
Output:

not panicking
no panic
panicking

func Rec

func Rec(ptr *error)

Must be deferred. Recovers from panics, writing the resulting error, if any, to the given pointer. Should be used together with "try"-style functions. Idempotently adds a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	someFunc := func() (err error) {
		defer try.Rec(&err)
		try.To(errors.New(`failure A`)) // Will panic, error will be returned.
		try.To(errors.New(`failure B`)) // Not executed; would panic.
		try.To(nil)                     // Not executed; would not panic.
		return
	}
	err := someFunc()
	fmt.Println(err)
}
Output:

failure A

func RecChan

func RecChan(errChan chan<- error)

Must be deferred. Version of `Rec` that sends the recovered error, if any, to the given channel. Idempotently adds a stacktrace.

Example
package main

import (
	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	someFunc := func(errChan chan error) {
		defer try.RecChan(errChan)
		try.To(errors.New(`failure A`)) // Will panic, error will be sent.
		try.To(errors.New(`failure B`)) // Not executed; would panic.
		try.To(nil)                     // Not executed; would not panic.
	}

	errs := make(chan error, 256)
	someFunc(errs)
}
Output:

func RecOnly added in v0.1.2

func RecOnly(ptr *error, test func(error) bool)

Must be deferred. Filtered version of `Rec`. Recovers from panics that satisfy the provided test. Re-panics on non-nil errors that don't satisfy the test. Does NOT check errors that are returned normally, without a panic. Should be used together with "try"-style functions. Idempotently adds a stacktrace.

Example
package main

import (
	"fmt"
	"os"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	isErrNoFile := func(err error) bool {
		return errors.Is(err, os.ErrNotExist)
	}

	someFunc := func() (err error) {
		defer try.RecOnly(&err, isErrNoFile)
		_ = try.ByteSlice(os.ReadFile(`non-existent-file`))
		fmt.Println(`file exists`)
		return
	}

	err := someFunc()
	fmt.Println(err)
}
Output:

open non-existent-file: no such file or directory

func RecWith

func RecWith(fun func(error))

Must be deferred. Recovery for background goroutines that have nowhere to return their error. Unlike the other "rec" functions, this doesn't send the error anywhere; instead it calls the provided function ONLY if the error was non-nil.

Functions that CAN return errors should use the other "rec" functions instead.

Example
package main

import (
	"log"

	"github.com/mitranim/try"
)

func main() {
	bgFun := func() {
		defer try.RecWith(func(err error) {
			log.Printf("failed to X: %+v\n", err)
		})
		panic("fail")
	}
	go bgFun()
}
Output:

func RecWithMessage

func RecWithMessage(ptr *error, msg string)

Must be deferred. Combination of `Rec` and `WithMessage`. Recovers from panics and adds a message. Idempotently adds a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	someFunc := func() (err error) {
		defer try.RecWithMessage(&err, `failed to X`)
		try.To(errors.New(`failure A`)) // Will panic, error will be wrapped and returned.
		try.To(errors.New(`failure B`)) // Not executed; would panic.
		try.To(nil)                     // Not executed; would not panic.
		return
	}
	err := someFunc()
	fmt.Println(err)
}
Output:

failed to X: failure A

func RecWithMessagef

func RecWithMessagef(ptr *error, pattern string, args ...interface{})

Must be deferred. Combination of `Rec` and `WithMessagef`. Recovers from panics and adds a message. Idempotently adds a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	someFunc := func() (err error) {
		defer try.RecWithMessagef(&err, `failed to %v`, `X`)
		try.To(errors.New(`failure A`)) // Will panic, error will be wrapped and returned.
		try.To(errors.New(`failure B`)) // Not executed; would panic.
		try.To(nil)                     // Not executed; would not panic.
		return
	}
	err := someFunc()
	fmt.Println(err)
}
Output:

failed to X: failure A

func Rune

func Rune(val rune, err error) rune

A "try" function that takes and returns a value of type `rune`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (rune, error) { return 255, nil }
	fmt.Println(try.Rune(someFunc()))
}
Output:

255

func RuneSlice

func RuneSlice(val []rune, err error) []rune

A "try" function that takes and returns a value of type `[]rune`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]rune, error) { return []rune{255}, nil }
	fmt.Println(try.RuneSlice(someFunc()))
}
Output:

[255]

func String

func String(val string, err error) string

A "try" function that takes and returns a value of type `string`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (string, error) { return "str", nil }
	fmt.Println(try.String(someFunc()))
}
Output:

str

func StringSlice

func StringSlice(val []string, err error) []string

A "try" function that takes and returns a value of type `[]string`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]string, error) { return []string{"val"}, nil }
	fmt.Println(try.StringSlice(someFunc()))
}
Output:

[val]

func To

func To(err error)

Simplifies control flow by panicking on non-nil errors. Should be used in conjunction with `Rec`.

If the error doesn't already have a stacktrace, adds one via "github.com/pkg/errors". Stacktraces are essential for such exception-like control flow. Without them, debugging would be incredibly tedious.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	someFunc := func() (err error) {
		defer try.Rec(&err)
		try.To(errors.New(`failure A`)) // Will panic, error will be returned.
		try.To(errors.New(`failure B`)) // Not executed; would panic.
		try.To(nil)                     // Not executed; would not panic.
		return
	}
	err := someFunc()
	fmt.Println(err)
}
Output:

failure A

func Trace

func Trace()

Must be deferred. Tool for adding a stacktrace to an arbitrary panic. Unlike the "rec" functions, this does NOT prevent the panic from propagating. It simply ensures that there's a stacktrace, then re-panics.

Caution: due to idiosyncrasies of `recover()`, this works ONLY when deferred directly. Anything other than `defer try.Trace()` will NOT work.

Example
package main

import (
	"github.com/mitranim/try"
)

func main() {
	defer try.Trace()
	if false {
		panic("unreachable")
	}
}
Output:

func Trans added in v0.1.2

func Trans(fun func(error) error)

Must be deferred. Short for "transmute" or "transform". Catches an ongoing panic, transforms the error by calling the provided function, and then re-panics via `To`. Can be used to ignore specific errors, by converting them to nil, which prevents the second panic. Idempotently adds a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	type ErrPub struct{ error }

	toErrPub := func(err error) error {
		if err != nil {
			return ErrPub{err}
		}
		return nil
	}

	someFunc := func() {
		defer try.Trans(toErrPub)
		panic(errors.New(`failure`))
	}

	err := try.Catch(someFunc)
	fmt.Println(errors.As(err, new(ErrPub)))
}
Output:

true

func Transing added in v0.1.5

func Transing(trans func(error) error, fun func())

Runs a function, "transmuting" the resulting panics by calling the provided transformer, which may choose to suppress or wrap specific error types. See `Trans`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	type ErrPub struct{ error }

	toErrPub := func(err error) error {
		if err != nil {
			return ErrPub{err}
		}
		return nil
	}

	someFunc := func() {
		panic(errors.New(`failure`))
	}

	err := try.Catch(func() {
		try.Transing(toErrPub, someFunc)
	})
	fmt.Println(errors.As(err, new(ErrPub)))
}
Output:

true

func Uint

func Uint(val uint, err error) uint

A "try" function that takes and returns a value of type `uint`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (uint, error) { return 255, nil }
	fmt.Println(try.Uint(someFunc()))
}
Output:

255

func Uint16

func Uint16(val uint16, err error) uint16

A "try" function that takes and returns a value of type `uint16`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (uint16, error) { return 255, nil }
	fmt.Println(try.Uint16(someFunc()))
}
Output:

255

func Uint16Slice

func Uint16Slice(val []uint16, err error) []uint16

A "try" function that takes and returns a value of type `[]uint16`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]uint16, error) { return []uint16{255}, nil }
	fmt.Println(try.Uint16Slice(someFunc()))
}
Output:

[255]

func Uint32

func Uint32(val uint32, err error) uint32

A "try" function that takes and returns a value of type `uint32`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (uint32, error) { return 255, nil }
	fmt.Println(try.Uint32(someFunc()))
}
Output:

255

func Uint32Slice

func Uint32Slice(val []uint32, err error) []uint32

A "try" function that takes and returns a value of type `[]uint32`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]uint32, error) { return []uint32{255}, nil }
	fmt.Println(try.Uint32Slice(someFunc()))
}
Output:

[255]

func Uint64

func Uint64(val uint64, err error) uint64

A "try" function that takes and returns a value of type `uint64`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (uint64, error) { return 255, nil }
	fmt.Println(try.Uint64(someFunc()))
}
Output:

255

func Uint64Slice

func Uint64Slice(val []uint64, err error) []uint64

A "try" function that takes and returns a value of type `[]uint64`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]uint64, error) { return []uint64{255}, nil }
	fmt.Println(try.Uint64Slice(someFunc()))
}
Output:

[255]

func Uint8

func Uint8(val uint8, err error) uint8

A "try" function that takes and returns a value of type `uint8`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (uint8, error) { return 255, nil }
	fmt.Println(try.Uint8(someFunc()))
}
Output:

255

func Uint8Slice

func Uint8Slice(val []uint8, err error) []uint8

A "try" function that takes and returns a value of type `[]uint8`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]uint8, error) { return []uint8{255}, nil }
	fmt.Println(try.Uint8Slice(someFunc()))
}
Output:

[255]

func UintSlice

func UintSlice(val []uint, err error) []uint

A "try" function that takes and returns a value of type `[]uint`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]uint, error) { return []uint{255}, nil }
	fmt.Println(try.UintSlice(someFunc()))
}
Output:

[255]

func Uintptr

func Uintptr(val uintptr, err error) uintptr

A "try" function that takes and returns a value of type `uintptr`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() (uintptr, error) { return 255, nil }
	fmt.Println(try.Uintptr(someFunc()))
}
Output:

255

func UintptrSlice

func UintptrSlice(val []uintptr, err error) []uintptr

A "try" function that takes and returns a value of type `[]uintptr`.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
)

func main() {
	someFunc := func() ([]uintptr, error) { return []uintptr{255}, nil }
	fmt.Println(try.UintptrSlice(someFunc()))
}
Output:

[255]

func WithMessage

func WithMessage(ptr *error, msg string)

Must be deferred. Wraps a non-nil error, prepending the message. Unlike `RecWithMessage`, does NOT implicitly recover or add a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	someFunc := func() (err error) {
		defer try.WithMessage(&err, `failed to X`)
		return errors.New(`failure A`) // Will be wrapped.
		return errors.New(`failure B`) // Not executed; would panic.
		return nil                     // Not executed; would not panic.
	}
	err := someFunc()
	fmt.Println(err)
}
Output:

failed to X: failure A

func WithMessagef

func WithMessagef(ptr *error, pattern string, args ...interface{})

Must be deferred. Wraps a non-nil error, prepending the message. Unlike `RecWithMessagef`, does NOT implicitly recover or add a stacktrace.

Example
package main

import (
	"fmt"

	"github.com/mitranim/try"
	"github.com/pkg/errors"
)

func main() {
	someFunc := func() (err error) {
		defer try.WithMessagef(&err, `failed to %v`, `X`)
		return errors.New(`failure A`) // Will panic, error will be wrapped.
		return errors.New(`failure B`) // Not executed; would panic.
		return nil                     // Not executed; would not panic.
	}
	err := someFunc()
	fmt.Println(err)
}
Output:

failed to X: failure A

func WithStack

func WithStack(err error) error

Adds a stacktrace via "github.com/pkg/errors", unless the error already has one. This exists because `errors.WithStack` ALWAYS wraps an error and adds a stacktrace, even when it would be redundant.

Should be used when it's unknown whether the error has a stacktrace. When the error is known to NOT have a stacktrace, use `errors.WithStack`, because this function adds its own frame, and `errors.WithStack` does not.

When called with `nil`, returns `nil`.

Types

type Val

type Val struct{ Value interface{} }

Used by `Err()` to wrap non-errors received from `recover()` and convert them to errors.

func (Val) Error

func (self Val) Error() string

Implement `error`.

func (Val) Unwrap

func (self Val) Unwrap() error

Implement error unwrapping, in case an `error` gets accidentally converted to `interface{}` before ending up here.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL