Перейти к пользовательским типам ошибок: зачем возвращать указатель на ошибку? [dубликат]

После тестирования различных решений на наушниках Dell XPS 15 все еще не было обнаружено, только для использования аналоговых внутренних динамиков (которые тоже не функционировали).

Затем я попробовал amixer из ответа @ Джереми, но ничего не произошло, и сразу после того, как я выполнил следующее из предложения пользователя Launchpad:

rm -r ~/.config/pulse/
pulseaudio -k && sudo alsa force-reload
sudo reboot

Я позволял наушникам подключаться к jackport все время, выполняя команды и пока Ноутбук перезагрузился.

После перезагрузки наушники воспроизводили звук, хотя они все еще не указаны в выходных устройствах, но указаны только внутренние аналоговые громкоговорители, но звук действительно поступает через наушники. Я не подключил наушники и не перезапустил систему с тех пор.

Кредит отправляется пользователю @ ответ Джереми на этот комментарий к ошибке Launchpad.

2
задан 14 May 2018 в 17:58

4 ответа

Мы можем посмотреть на это с точки зрения обработки ошибок, а не на создание ошибки.

Ошибка Определить историю стороны

type ErrType1 struct {}

func (e *ErrType1) Error() string {
    return "ErrType1"
}

type ErrType2 struct {}

func (e ErrType2) Error() string {
    return "ErrType1"
}

История обработчика ошибок

err :=  someFunc()
switch err.(type) {
case *ErrType1
   ...
case ErrType2, *ErrType2
   ...
default
   ...
}

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

Для ErrType2, оба &ErrType2{} и ErrType2{} удовлетворяют интерфейсу.

Поскольку someFunc возвращает интерфейс error, вы никогда не знаете, вернет ли он значение struct или указатель struct, особенно если someFunc isn

Поэтому, используя приемник указателя, он не останавливает пользователя от возврата указателя в качестве ошибки.

Было сказано, что все другие аспекты, такие как Stack vs. Heap (распределение памяти, давление в GC) все еще применяются.

Выберите свою реализацию в соответствии с вашими вариантами использования.

В общем, я предпочитаю приемник указателя по той причине, продемонстрировано выше. Я предпочитаю API-интерфейс Friendly API по производительности, а иногда, когда тип ошибки содержит огромную информацию, он более эффективен.

0
ответ дан 15 August 2018 в 16:49

Нет :)

https://blog.golang.org/error-handling-and-go#TOC_2.

Интерфейсы Go допускают все, что соответствует ошибке интерфейс, который должен обрабатываться кодом, ожидающим error

type error interface {
    Error() string
}

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

выделения (d10)

На случайном разглашении, Анекдотически, я лично считаю, что просмотр таких примеров, как этот, объясняется тем, почему новые разработчики go go предпочитают приемники указателей по умолчанию .

0
ответ дан 15 August 2018 в 16:49
  • 1
    Кстати, если ваша структура предназначена для использования внутри интерфейса, по умолчанию все равно должен быть приемником значения . Приемники-указатели могут выполнять только интерфейсы, если указатель передается в интерфейс. Ценностные приемники работают для указателей или значений. [D1] Подробнее – Kaedys 14 May 2018 в 18:07

Ошибки в go удовлетворяют только интерфейсу ошибок, то есть предоставляют метод .Error(). Создавая пользовательские ошибки или выкапывая исходный код Go, вы обнаружите, что ошибки намного больше за кулисами. Если в вашем приложении заполняется структура, чтобы избежать создания копий в памяти, более эффективно передавать ее как указатель. Кроме того, как показано в книге языков программирования Go Go:

Функция fmt.Errorf форматирует сообщение об ошибке с помощью fmt.Sprintf и возвращает новое значение ошибки. Мы используем его для создания описательных ошибок путем последовательного префикса дополнительной информации контекста к исходному сообщению об ошибке. Когда ошибка в конечном счете обрабатывается основной функцией программы, она должна обеспечить четкую причинную цепочку от корневой проблемы до общего сбоя, напоминающего об обнаружении аварии НАСА:

genesis: crashed: no parachute: G-switch failed: bad relay orientation

Функция fmt.Errorf форматирует сообщение об ошибке с помощью fmt.Sprintf и возвращает новое значение ошибки. Мы используем его для создания описательных ошибок путем последовательного префикса дополнительной информации контекста к исходному сообщению об ошибке. Когда ошибка, в конечном счете, обрабатывается основной функцией программы, она должна обеспечить четкую причинную цепочку от корневой проблемы до общей неудачи, напоминающую об обнаружении аварии НАСА:

Из этого можно что если один «тип ошибки» содержит большое количество информации, и, кроме того, мы объединяем их вместе для создания подробного сообщения, использование указателей будет лучшим способом для этого.

1
ответ дан 15 August 2018 в 16:49

Во-первых, сообщение в блоге, которое вы связали и взяли ваш пример, appError не является error. Это оболочка, несущая значение ошибки и другую связанную информацию, используемую при реализации примеров, они не отображаются, а appError и *appError никогда не используются как значение error.

Таким образом, приведенный вами пример не имеет никакого отношения к вашему фактическому вопросу. Но чтобы ответить на вопрос в заголовке:

В общем, причиной может быть последовательность. Если тип имеет много методов, а некоторые - приемник указателей (например, потому что они изменяют значение), часто полезно объявить все методы с помощью получателя указателя, поэтому нет путаницы в отношении наборов методов типа и типа указателя.

Ответы на реализацию error: когда вы используете значение struct для реализации значения error, опасно использовать не указатель для реализации интерфейса error. Почему это так?

Так как метод устанавливает , это интерфейс. И значения интерфейса сопоставимы. И их сравнивают, сравнивая значения, которые они обертывают. несущие . Если вы храните в них указатели, значения ошибок будут равны, если они сохранят один и тот же указатель. И если вы сохраняете в них не указатели (structs), они равны, если значения struct равны.

Чтобы продумать это и показать пример:

Стандартная библиотека имеет сопоставимый пакет . Вы можете создавать значения ошибок из значений string с помощью функции errors.New(). Если вы посмотрите на его реализацию (errors/errors.go), это просто:

// Package errors implements functions to manipulate errors.
package errors

// New returns an error that formats as the given text.
func New(text string) error {
    return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

Реализация возвращает указатель на очень простое значение структуры. Это так, что если вы создадите 2 значения ошибок с тем же значением string, они не будут равны:

e1 := errors.New("hey")
e2 := errors.New("hey")
fmt.Println(e1, e2, e1 == e2)

Выход:

hey hey false

Это намеренно .

Теперь, если вы вернете не указатель:

func New(text string) error {
    return errorString{text}
}

type errorString struct {
    s string
}

func (e errorString) Error() string {
    return e.s
}

2 значения ошибки с тем же string будут равны:

e1 = New("hey")
e2 = New("hey")
fmt.Println(e1, e2, e1 == e2)

Выход:

hey hey true

Попробуйте примеры на errors.New() .

Яркий пример, почему это важно: посмотрите на сохраненное значение ошибки в переменной io.EOF:

var EOF = errors.New("EOF")

Ожидается, что реализации io.EOF возвращают это конкретное значение ошибки в конец сигнала ввода. Поэтому вы можете спокойно сравнить ошибку, возвращенную Reader.Read(), на io.EOF, чтобы узнать, достигнут ли конец ввода. Вы можете быть уверены, что если они время от времени возвращают пользовательские ошибки, они никогда не будут равны io.EOF, это гарантирует errors.New() (потому что он возвращает указатель на неизведанное значение структуры).

2
ответ дан 15 August 2018 в 16:49
  • 1
    Отличный подробный ответ. Сотрудник подумал, что если мы будем использовать не указатель, мы не сможем сделать обычный if err != nil - это правильно? – Nathan H 15 May 2018 в 10:05
  • 2
    @NathanH Нет, это неправильно. nil не является допустимым значением для structs, это правда, но если вы работаете с error значениями (которые могут обернуть вашу структуру без указателя), error является типом интерфейса, который может иметь значение nil (это нулевое значение для типов интерфейсов). Но я рекомендую прочитать этот ответ, чтобы разобраться в этом: Скрывая значения nil, понимая, почему golang терпит неудачу здесь . – icza 15 May 2018 в 10:12

Другие вопросы по тегам:

Похожие вопросы: