Достать тип ошибки в Go
Конкретный тип достают из ошибки, даже если
она обернута в другую. Снаружи виден общий
интерфейс error и один текст, а поля
исходной структуры нужны для логики: путь,
код, внутренняя причина. Для этого после
проверки на nil используют команду
из пакета errors, которая ищет тип
в цепочке оберток.
Сначала посмотрим, как внешний текст
сохраняет связь с исходной ошибкой через
%w в fmt.Errorf:
package main
import (
"errors"
"fmt"
)
type PathError struct {
Path string
Err error
}
func (e *PathError) Error() string {
return e.Path + ": " + e.Err.Error()
}
func main() {
inner := &PathError{
Path: "main.go",
Err: errors.New("fail"),
}
wrapped := fmt.Errorf("read failed: %w", inner)
fmt.Println(wrapped)
}
Печать показывает и внешнюю фразу, и текст
внутренней ошибки. Место %w⁆ в шаблоне
кладет исходное значение внутрь новой
обертки, чтобы позже можно было дойти до
PathError. Команда errors.Is
из основного курса сравнивает с готовым
значением, а команда errors.As
достает тип.
Достанем PathError из обернутой
ошибки. Второй аргумент команды As
это адрес переменной типа *PathError,
без лишнего уровня указателя:
package main
import (
"errors"
"fmt"
)
type PathError struct {
Path string
Err error
}
func (e *PathError) Error() string {
return e.Path + ": " + e.Err.Error()
}
func main() {
inner := &PathError{
Path: "main.go",
Err: errors.New("fail"),
}
wrapped := fmt.Errorf("read failed: %w", inner)
var pe *PathError
if errors.As(wrapped, &pe) {
fmt.Println(pe.Path)
}
}
На экране "main.go". Переменная pe
получила указатель на исходную структуру.
Если тип в цепочке не найден, условие ложно
и pe остается nil.
Тот же прием работает, когда оберток несколько,
пока внутри сохранена связь через %w:
package main
import (
"errors"
"fmt"
)
type PathError struct {
Path string
Err error
}
func (e *PathError) Error() string {
return e.Path + ": " + e.Err.Error()
}
func main() {
inner := &PathError{
Path: "main.go",
Err: errors.New("fail"),
}
mid := fmt.Errorf("layer: %w", inner)
outer := fmt.Errorf("top: %w", mid)
var pe *PathError
if errors.As(outer, &pe) {
fmt.Println(pe.Path)
}
}
Дана ошибка:
package main
import (
"errors"
"fmt"
)
type PathError struct {
Path string
Err error
}
func (e *PathError) Error() string {
return e.Path + ": " + e.Err.Error()
}
inner := &PathError{
Path: "main.go",
Err: errors.New("fail"),
}
err := fmt.Errorf("open: %w", inner)
Запишите в path поле Path
из PathError внутри err
и выведите path.
Даны переменные:
package main
import (
"errors"
"fmt"
)
type PathError struct {
Path string
Err error
}
func (e *PathError) Error() string {
return e.Path + ": " + e.Err.Error()
}
pe := &PathError{
Path: "data.txt",
Err: errors.New("denied"),
}
wrapped := fmt.Errorf("copy: %w", pe)
Проверьте, лежит ли в wrapped
тип PathError, и выведите
true или false.
Скажите, какой тип у второго аргумента
команды As, если внутри нужен
*PathError.