Должен ли я использовать% s или% v для форматирования ошибок - PullRequest
2 голосов
/ 18 апреля 2020

Оба %s и %v могут использоваться для форматирования ошибок в Go, и, по-видимому, функциональных различий, по крайней мере внешне, нет.

Мы видим оба в Go. собственные инструменты.

In cmd / go / internal / get / path. go: return fmt.Errorf("malformed import path %q: %v", path, err)

In cmd / go / internal / list /list.go: base.Fatalf("%s", err)

Стоит ли мне когда-нибудь отдавать предпочтение одному?

Ответы [ 2 ]

4 голосов
/ 18 апреля 2020

Следует ли использовать% s или% v для форматирования ошибок?

TL; DR; Ни то, ни другое. Используйте %w в 99,99% случаев. В остальных 0,001% случаев %v и %s, вероятно, "должны" вести себя одинаково, за исключением случаев, когда значение ошибки равно nil, но нет никаких гарантий. Более дружественный вывод %v для nil ошибок может быть причиной для предпочтения %v (см. Ниже).

Теперь для деталей:

Используйте %w вместо %v или %s:

Начиная с Go 1.13 (или ранее, если вы используете golang .org / x / xerrors ), вы можете использовать только глагол %w для значений error, которые обертывают ошибку так, чтобы впоследствии ее можно было развернуть с помощью errors.Unwrap, и чтобы ее можно было рассмотреть с errors.Is и errors.As.

Единственные случаи, когда это неуместно :

  1. Вы должны поддерживать более старую версию Go, и xerrors не вариант.
  2. Вы хотите создать уникальную ошибку, а не обернуть существующий. Это может быть целесообразно, например, если вы получаете ошибку Not found из своей базы данных при поиске пользователя и хотите преобразовать ее в Unauthorized ответ. В таком случае редко можно использовать исходное значение ошибки с любым глаголом форматирования, однако.

Хорошо, так что насчет %v и * 1048? *?

Подробная информация о том, как реализованы %s и %v, доступна в документации . Я выделил части, относящиеся к вашему вопросу.

  1. Если операнд является отражением. Значение, операнд заменяется конкретным значением, которое он содержит, и печать продолжается со следующим правилом.

  2. Если операнд реализует интерфейс Formatter, он будет вызван. Formatter обеспечивает точный контроль форматирования.

  3. Если глагол% v используется с флагом # (% # v) и операнд реализует интерфейс GoStringer, он будет вызван.

    Если формат ( что неявно% v для Println et c.) допустимо для строки (% s% q% v% x% X), применяются следующие два правила :

  4. Если в операнде реализован интерфейс ошибок, будет вызван метод Error для преобразования объекта в строку, которая затем будет отформатирована в соответствии с требованиями глагола (если есть).

  5. Если операнд реализует метод String () string, этот метод будет Он вызвал для преобразования объекта в строку, которая затем будет отформатирована в соответствии с требованиями глагола (если есть).

Подводя итог, fmt.*f функции будут:

  1. Искать метод Format(), и если он существует, они будут его вызывать.
  2. Искать метод Error(), и если он существует, они вызовут его.
  3. Найдите метод String(), и, если он существует, вызовите его.
  4. Используйте форматирование по умолчанию.

Так что в На практике это означает, что %s и %v идентичны, за исключением случаев, когда существует метод Format() для типа ошибки (или когда ошибка nil). Когда у ошибки есть метод Format(), можно надеяться, что он выдаст тот же результат с %s, %v и err.Error(), но, поскольку это зависит от реализации ошибки, нет гарантии и, следовательно, никакого «правильного ответа» здесь.

И, наконец, если ваш тип ошибки поддерживает вариант глагола %+v, то вам, конечно, нужно будет это использовать, если вы хотите получить подробный вывод .

nil значения

Хотя редко (намеренно) вызывать fmt.*f при ошибке nil, поведение между %s и %v:

%s: %!s(<nil>)
%v: <nil>

ссылка на игровую площадку

0 голосов
/ 18 апреля 2020

Используйте %v для значения ошибки.

if err != nil {
    return fmt.Errorf("pack %v: %v", name, err)
}

Но в Go 1.13 функция fmt.Errorf поддерживает новый глагол %w. Когда этот глагол присутствует, ошибка, возвращаемая fmt.Errorf, будет иметь метод Unwrap, возвращающий аргумент %w, который должен быть ошибкой. Во всех других отношениях %w идентичен %v.

if err != nil {
    // Return an error which unwraps to err.
    return fmt.Errorf("pack %v: %w", name, err)
}

Места, где необходимо различать %w и %v:

Читать комментарии в кодовом блоке

f, err := os.Open(filename)
if err != nil {
    // The *os.PathError returned by os.Open is an internal detail.
    // To avoid exposing it to the caller, repackage it as a new
    // error with the same text.
    //
    //
    // We use the %v formatting verb, since
    // %w would permit the caller to unwrap the original *os.PathError.
    return fmt.Errorf("%v", err)
}

Чтение: При ошибке при переключении на w


Кроме того, встроенный интерфейс ошибок позволяет Go программистам добавлять любую информацию, которую они хотят. Все, что для этого требуется, - это тип, который реализует метод Error

Пример:

type QueryError struct {
    Query string
    Err   error
}

func (e *QueryError) Error() string { return e.Query + ": " + e.Err.Error() }

Итак, в большинстве случаев в большинстве примеров реализован аналогичный тип, где err имеет Error метод, который возвращает string, для которого вы можете использовать %s

...