Golang error wrap / unrap && проверка типов с ошибками. Is () - PullRequest
0 голосов
/ 18 июня 2020

Я проверяю трассировку ошибок в Go v1.13 Go v1.14. Почему кажется, что с помощью errors.Is() можно найти только реализации ошибок без параметров или с приемниками значений? Это означает, что реализация ошибки с возможностью обертывания должна иметь приемник значения, чтобы ее можно было найти с помощью errors.Is().

package main

import (
    "fmt"
    "errors"
)

type someAtomicError struct {}
func (e *someAtomicError) Error() string { return "Hi!" }
func checkAtomicError() {
    e := &someAtomicError{}
    e2 := fmt.Errorf("whoa!: %w", e)
    e2IsE := errors.Is(e2, &someAtomicError{})
    fmt.Println("atomic error trace ---\t\t", e2, "\t\t--- is traceable: ", e2IsE)
}


type someWrapperError struct {
    Msg string
    Err error
}
func (e someWrapperError) Error() string { return fmt.Sprintf("%s: %v", e.Msg, e.Err) }
func (e someWrapperError) Unwrap() error { return e.Err }
func checkWrapperError() {
    e := someWrapperError{"Hi!", nil}
    e2 := fmt.Errorf("whoa!: %w", e)
    e2IsE := errors.Is(e2, someWrapperError{"Hi!", nil})
    fmt.Println("wrapper error trace ---\t\t", e2, "\t--- is traceable: ", e2IsE)
}


type somePointerWrapperError struct {
    Msg string
    Err error
}
func (e *somePointerWrapperError) Error() string { return fmt.Sprintf("%s: %v", e.Msg, e.Err) }
func (e *somePointerWrapperError) Unwrap() error { return e.Err }
func checkPointerWrapperError() {
    e := &somePointerWrapperError{"Hi!", nil}
    e2 := fmt.Errorf("whoa!: %w", e)
    e2IsE := errors.Is(e2, &somePointerWrapperError{"Hi!", nil})
    fmt.Println("pointer wrapper error trace ---\t", e2, "\t--- is traceable: ", e2IsE)
}


func main() {
    checkAtomicError()
    checkWrapperError() 
    checkPointerWrapperError()
}

//atomic error trace ---         whoa!: Hi!         --- is traceable:  true
//wrapper error trace ---        whoa!: Hi!: <nil>  --- is traceable:  true
//pointer wrapper error trace ---    whoa!: Hi!: <nil>  --- is traceable:  false

https://play.golang.org/p/-hSukZ-gii2

Изменить: похоже, что любая разница в параметрах, в том числе в обернутом параметре ошибки, Err, приведет к тому, что тип не может быть найден с помощью errors.Is().

1 Ответ

1 голос
/ 18 июня 2020

Причина, по которой вы получаете false при попытке найти одну ошибку в другой с помощью errors.Is, заключается в том, что - хотя две ошибки могут иметь одинаковые значения полей - это два разных указателя памяти:

e := &somePointerWrapperError{"Hi!", nil}
ew := fmt.Errorf("whoa!: %w", e)

e2 := &somePointerWrapperError{"Hi!", nil} // e2 != e   

errors.Is(ew, e2) // false - because `ew` wraps `e` not `e2`
errors.Is(ew, e)  // true

Итак, как определить этот «тип» ошибки и получить ее значение: используйте вместо этого errors.As:

e := &somePointerWrapperError{"Hi!", nil}
e2 := fmt.Errorf("whoa!: %w", e)

var ev *somePointerWrapperError
if errors.As(e2, &ev) {
    fmt.Printf("%#v\n", ev) // &somePointerWrapperError{Msg:"Hi!", Err:error(nil)}
}

https://play.golang.org/p/CttKThLasXD

...