package main
import (
"errors"
"fmt"
)
type CodeError struct {
Code int
}
func (e *CodeError) Error() string {
return fmt.Sprintf("code=%d", e.Code)
}
type MaskError struct {
Value int
}
func (e MaskError) Error() string {
return "masked"
}
func (e MaskError) As(target any) bool {
dst, ok := target.(**CodeError)
if !ok {
return false
}
*dst = &CodeError{Code: e.Value}
return true
}
func main() {
err := errors.Join(
fmt.Errorf("left: %w", MaskError{Value: 10}),
errors.Join(
&CodeError{Code: 20},
fmt.Errorf("right: %w", &CodeError{Code: 30}),
),
)
result, ok := errors.AsType[*CodeError](err)
fmt.Println(ok, result.Code)
}
Что будет выведено?
true 10
Кажется, ответ должен быть 20: это первая настоящая ошибка типа *CodeError. Но errors.AsType обходит дерево в глубину, начиная с первой ветки.
MaskError встречается раньше и через собственный метод As притворяется *CodeError, записывая значение с кодом 10. Поэтому дальнейший поиск прекращается.
Cлучай, когда тип ошибки определяется не только её реальным типом, но и поведением метода As.