вторник, 30 июня 2020 г.

Пакет errors в Golang

Пакет errors реализует функции для манипулирования ошибками.

Функция New создает ошибки, единственным содержимым которых является текстовое сообщение.

Функции Unwrap, Is и As работают с ошибками, которые могут переносить другие ошибки. Ошибка переносит другую ошибку, если ее тип имеет метод

Unwrap() error

Если e.Unwrap() возвращает ненулевую ошибку w, то мы говорим, что e переносит w.

Unwrap распаковывает упакованные ошибки. Если тип его аргумента имеет метод Unwrap, он вызывает метод один раз. В противном случае возвращается nil.

Простой способ создать упакованные ошибки - вызвать fmt.Errorf и применить глагол %w к аргументу ошибки:

errors.Unwrap(fmt.Errorf("... %w ...", ..., err, ...))

возвращает err.

Is разворачивает свой первый аргумент, последовательно ища ошибку, соответствующую второму. Он сообщает, находит ли он совпадение. Это следует использовать вместо простых проверок на равенство:

if errors.Is(err, os.ErrExist)

предпочтительнее чем

if err == os.ErrExist

потому что первый преуспеет, если err обернет os.ErrExist.

As разворачивает свой первый аргумент последовательно ищет ошибку, которая может быть назначена второму аргументу, который должен быть указателем. Если это успешно, выполняется назначение и возвращает true. В противном случае возвращается false. Форма

var perr *os.PathError
if errors.As(err, &perr) {
    fmt.Println(perr.Path)
}

предпочтительнее чем

if perr, ok := err.(*os.PathError); ok {
    fmt.Println(perr.Path)
}

потому что первый будет успешным, если err обернет *os.PathError.

Пример использования пользовательского типа ошибки

package main

import (
    "fmt"
    "time"
)

// MyError это реализация error 
// которая включает время и сообщение.
type MyError struct {
    When time.Time
    What string
}

func (e MyError) Error() string {
    return fmt.Sprintf("%v: %v", e.When, e.What)
}

func oops() error {
    return MyError{
        time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC),
        "the file system has gone away",
    }
}

func main() {
    if err := oops(); err != nil {
        fmt.Println(err)
    }
}

Вывод:

1989-03-15 22:30:00 +0000 UTC: the file system has gone away

Функция New

func New(text string) error

New возвращает ошибку, которая форматируется как заданный текст. Каждый вызов New возвращает отдельное значение ошибки, даже если текст идентичен.

package main

import (
    "errors"
    "fmt"
)

func main() {
    err := errors.New("Неожиданная ошибка")
    if err != nil {
        fmt.Print(err)
    }
}

Вывод:

Неожиданная ошибка

Функция Errorf пакета fmt позволяет использовать функции форматирования пакета для создания описательных сообщений об ошибках.

package main

import (
    "fmt"
)

func main() {
    const name, id = "бублик", 117
    err := fmt.Errorf("пользователь %q (id %d) не найден", name, id)
    if err != nil {
        fmt.Print(err)
    }
}

Вывод:

пользователь "бублик" (id 117) не найден

Функция Is (с версии Go 1.13)

func Is(err, target error) bool

Is сообщает, соответствует ли target ошибке какая-либо ошибка в цепочке err.

Цепочка состоит из самой err, за которой следует последовательность ошибок, получаемых повторным вызовом Unwrap.

Считается, что ошибка соответствует цели, если она равна этой цели или если она реализует метод Is(error) bool, такой что Is(target) возвращает true.

Тип ошибки может предоставлять метод Is, поэтому его можно рассматривать как эквивалент существующей ошибки. Например, если MyError определяет

func (m MyError) Is(target error) bool { return target == os.ErrExist }

затем Is (MyError{}, os.ErrExist) возвращает значение true. syscall.Errno.Is - пример в стандартной библиотеке.

Функция As (с версии Go 1.13)

func As(err error, target interface{}) bool

As находит первую ошибку в цепочке err, которая соответствует target, и если находит, то устанавливает target равным этому значению ошибки и возвращает true. В противном случае возвращается false.

Цепочка состоит из самой err, за которой следует последовательность ошибок, получаемых повторным вызовом Unwrap.

Ошибка соответствует цели, если конкретное значение ошибки присваивается значению, на которое указывает цель, или если ошибка имеет метод As(interface{}) bool, такой что As(target) возвращает true. В последнем случае метод As отвечает за установку цели.

Тип ошибки может предоставлять метод As, поэтому его можно обрабатывать так, как если бы это был другой тип ошибки.

As паникует, если target не является ненулевым указателем ни на тип, который реализует error, ни на любой тип интерфейса.

Пример использования As

package main

import (
    "errors"
    "fmt"
    "os"
)

func main() {
    if _, err := os.Open("non-existing"); err != nil {
        var pathError *os.PathError
        if errors.As(err, &pathError) {
            fmt.Println("Failed at path:", pathError.Path)
        } else {
            fmt.Println(err)
        }
    }

}

Вывод:

Failed at path: non-existing

Функция Unwrap

func Unwrap(err error) error

Unwrap возвращает результат вызова метода Unwrap для err, если тип err содержит метод Unwrap, возвращающий ошибку. В противном случае Unwrap возвращает nil.


Читайте также:


Комментариев нет:

Отправить комментарий