英文:
Go - How to combine multiple error objects
问题
假设你想将strconv.Atoi
返回的错误与你的命名错误ErrSomething
结合起来,以便在不丢失底层错误信息的情况下,使用你的错误"常量"来检查出错的具体原因。你可以使用fmt.Errorf
函数来实现这一点。以下是修改后的代码示例:
package lib
import (
"errors"
"fmt"
"strconv"
)
var ErrSomething = errors.New("foobar")
func SomeFunc(str string) (int, error) {
i, err := strconv.Atoi(str)
if err != nil {
return 0, fmt.Errorf("%w: %v", ErrSomething, err)
}
// 进行其他操作,可能返回其他错误
return i, nil
}
在这个示例中,我们使用fmt.Errorf
函数来将ErrSomething
和err
结合起来。%w
占位符用于将错误包装为一个Unwrap
错误,以便在后续的错误处理中使用errors.Is
函数进行检查。通过这种方式,你的用户可以使用errors.Is(err, ErrSomething)
来检查错误是否是ErrSomething
。
英文:
Say I have the following code:
package lib
import (
"errors"
"strconv"
)
var ErrSomething = errors.New("foobar")
func SomeFunc(str string) (int, error) {
i, err := strconv.Atoi(str)
if err != nil {
// how to combine ErrSomething with err?
return 0, fmt.Errorf("%w: %w", ErrSomething, err)
}
// do other things, potentially return other errors
return i
}
How do I combine the error returned from strconv.Atoi
with my named error ErrSomething
. The reason for combining is so that users of my SomeFunc()
can check what exactly went wrong using my error "constants" while not losing information about the underlying error.
I have read similar questions but the usual answer is to just do: return 0, fmt.Errorf("foobar: %w", err)
but this way my users can't check the error using errors.Is(err, ???)
答案1
得分: 4
你可以通过创建一个实现Is
和Unwrap
方法的错误类型来实现所需的行为,示例如下:
package lib
import (
"fmt"
"strconv"
)
type FoobarError struct {
msg string
original error
}
func (err *FoobarError) Error() string {
return fmt.Sprintf("%s: %s", err.msg, err.original.Error())
}
func (err *FoobarError) Unwrap() error {
return err.original
}
func (err *FoobarError) Is(target error) bool {
_, ok := target.(*FoobarError)
return ok
}
func SomeFunc() error {
// 这里使用strconv.ErrSyntax作为一个虚拟错误,用于表示可能由strconv.Atoi或其他操作返回的错误。
err := strconv.ErrSyntax
return &FoobarError{"foobar", err}
}
用法:
package main
import (
"errors"
"fmt"
"strconv"
"lib"
)
func main() {
if err := lib.SomeFunc(); err != nil {
fmt.Println(err) // foobar: invalid syntax
fmt.Println(errors.Is(err, &lib.FoobarError{})) // true
fmt.Println(errors.Is(err, strconv.ErrSyntax)) // true
}
}
你可以在这里了解更多关于这种方法的信息。
附加内容
类似于Go的os.IsExist,你可能有兴趣在你的库中添加一个辅助函数,以便用户更容易地检查错误:
package lib
import (
"errors"
// ...
)
// ...
func IsFoobar(err error) bool {
return errors.Is(err, &FoobarError{})
}
用法:
package main
// ...
func main() {
err := lib.SomeFunc();
if lib.IsFoobar(err) {
// ...
}
}
英文:
You can achieve the desired behavior by creating an error type that implements the Is
and Unwrap
methods as follows:
package lib
import (
"fmt"
"strconv"
)
type FoobarError struct {
msg string
original error
}
func (err *FoobarError) Error() string {
return fmt.Sprintf("%s: %s", err.msg, err.original.Error())
}
func (err *FoobarError) Unwrap() error {
return err.original
}
func (err *FoobarError) Is(target error) bool {
_, ok := target.(*FoobarError)
return ok
}
func SomeFunc() error {
// strconv.ErrSyntax is used as a dummy error here for the error
// that might be returned by strconv.Atoi or any other operation.
err := strconv.ErrSyntax
return &FoobarError{"foobar", err}
}
Usage:
package main
import (
"errors"
"fmt"
"strconv"
"lib"
)
func main() {
if err := lib.SomeFunc(); err != nil {
fmt.Println(err) // foobar: invalid syntax
fmt.Println(errors.Is(err, &lib.FoobarError{})) // true
fmt.Println(errors.Is(err, strconv.ErrSyntax)) // true
}
}
You can read more about this approach here.
Bonus
Similar to Go's os.IsExist, you may be interested in adding a helper function to your library that makes it easier for the user to check the error:
package lib
import (
"errors"
// ...
)
// ...
func IsFoobar(err error) bool {
return errors.Is(err, &FoobarError{})
}
Usage:
package main
// ...
func main() {
err := lib.SomeFunc();
if lib.IsFoobar(err) {
// ...
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论