Вопрос

А Go создатели языка писать:

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

Каково ваше мнение по этому поводу?

Это было полезно?

Решение

Нет, в этом нет ничего плохого. assert пока вы используете его по назначению.

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

  • Утверждать:Сбой в самой логике программы.
  • Обработка ошибок:Ошибочный ввод или состояние системы не связано с ошибкой в ​​программе.

Другие советы

Нет, ни goto ни assert злые.Но и то, и другое можно использовать неправильно.

Assert предназначен для проверки работоспособности.Вещи, которые должны убить программу, если они неверны.Не для проверки или замены обработки ошибок.

По этой логике точки останова тоже зло.

Утверждения следует использовать как средство отладки и ничего больше.«Зло» — это когда ты пытаешься их использовать вместо обработки ошибок.

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

Они не имеют никакого отношения к обработке ошибок, но, к сожалению, некоторые программисты злоупотребляют ими как таковыми, а затем объявляют их «злыми».

Мне очень нравится использовать Assert.Я считаю это очень полезным, когда впервые создаю приложения (возможно, для нового домена).Вместо очень сложной проверки ошибок (которую я бы посчитал преждевременной оптимизацией) я пишу быстро и добавляю много утверждений.После того, как я узнаю больше о том, как все работает, я переписываю и удаляю некоторые утверждения и изменяю их для лучшей обработки ошибок.

Благодаря утверждениям я трачу гораздо меньше времени на кодирование/отладку программ.

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

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

При правильном использовании они нет зло.

В качестве дополнительной информации go предоставляет встроенную функцию panic.Это можно использовать вместо assert.Например.

if x < 0 {
    panic("x is less than 0");
}

panic напечатает трассировку стека, поэтому в некотором смысле это имеет целью assert.

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

build-sorted-list-from-user-input(input)

    throw-exception-if-bad-input(input)

    ...

    //build list using algorithm that you expect to give a sorted list

    ...

    assert(is-sorted(list))

end

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

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

Утверждения не являются злом, но ими можно легко злоупотребить.Я согласен с утверждением, что «утверждения часто используются как опора, позволяющая не думать о правильной обработке ошибок и отчетах».Я видел это довольно часто.

Лично мне нравится использовать утверждения, потому что они документируют предположения, которые я мог сделать во время написания кода.Если эти предположения нарушаются при сохранении кода, проблема может быть обнаружена во время тестирования.Тем не менее, я стараюсь удалять все утверждения из моего кода при выполнении производственной сборки (т. е. с использованием #ifdefs).Удаляя утверждения из производственной сборки, я исключаю риск того, что кто-либо злоупотребит ими в качестве опоры.

Есть еще одна проблема с утверждениями.Утверждения проверяются только во время выполнения.Но часто бывает так, что проверку, которую вы хотели бы выполнить, можно было бы выполнить во время компиляции.Предпочтительно обнаружить проблему во время компиляции.Для программистов C++ boost предоставляет BOOST_STATIC_ASSERT, который позволяет вам это сделать.Для программистов на языке C эта статья ( текст ссылки ) описывает технику, которую можно использовать для выполнения утверждений во время компиляции.

Подводя итог, я придерживаюсь следующего эмпирического правила:Не используйте утверждения в производственной сборке и, если возможно, используйте утверждения только для тех вещей, которые не могут быть проверены во время компиляции (т. е. должны быть проверены во время выполнения).

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

Они особенно полезны, если вы хотите следовать принципу «раннего сбоя».Например, предположим, что вы реализуете механизм подсчета ссылок.Вы знаете, что в определенных местах вашего кода счетчик ссылок должен быть равен нулю или единице.А также предположим, что если счетчик ссылок неправильный, программа выйдет из строя не сразу, а во время следующего цикла сообщений, и в этот момент будет трудно выяснить, почему что-то пошло не так.Утверждение было бы полезно для обнаружения ошибки ближе к ее источнику.

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

Однако полезно выполнить отладчик по условию и получить всю информацию о файле/строке, а также точное выражение и точное значение.

Наличие утверждения, которое «оценивает состояние только при отладке», может быть оптимизацией производительности и, как таковое, полезно только в 0,0001% программ, где люди знают, что делают.Во всех остальных случаях это вредно, так как выражение может фактически изменить состояние программы:

assert(2 == ShroedingersCat.GetNumEars()); заставит программу выполнять разные действия при отладке и выпуске.

Мы разработали набор макросов утверждения, которые будут генерировать исключение, и делаем это как в отладочной, так и в выпускной версии.Например, THROW_UNLESS_EQ(a, 20); выдаст исключение с сообщением What(), имеющим как файл, строку, так и фактические значения а и так далее.Только макрос сможет это сделать.Отладчик может быть настроен на прерывание при возникновении исключения определенного типа.

Я не люблю резких заявлений.Хотя я бы не стал говорить, что они злые.

По сути, утверждение будет делать то же самое, что и непроверяемое исключение, единственное исключение состоит в том, что утверждение (обычно) не должно сохраняться для конечного продукта.

Если вы создаете систему безопасности для себя во время отладки и сборки системы, зачем вам запрещать эту систему безопасности вашему клиенту, вашей службе поддержки или кому-либо, кто сможет использовать программное обеспечение, которое вы сейчас создаете?Используйте исключения исключительно как для утверждений, так и для исключительных ситуаций.Создав соответствующую иерархию исключений, вы сможете очень быстро отличить одно от другого.За исключением этого случая, утверждение остается на месте и может предоставить ценную информацию в случае сбоя, которая в противном случае была бы потеряна.

Так что я полностью понимаю создателей Go, которые вообще удалили утверждения и заставили программистов использовать исключения для решения ситуации.Этому есть простое объяснение: исключения — это просто лучший механизм для работы, зачем придерживаться архаичных утверждений?

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

Недавно я начал добавлять в свой код некоторые утверждения, и вот как я это делаю:

Я мысленно делю свой код на граничный код и внутренний код.Граничный код — это код, который обрабатывает ввод пользователя, читает файлы и получает данные из сети.В этом коде я запрашиваю ввод в цикле, который завершается только тогда, когда ввод действителен (в случае интерактивного пользовательского ввода), или выдаю исключения в случае невосстановимого повреждения файла/сети.

Внутренний код – это все остальное.Например, функция, которая устанавливает переменную в моем классе, может быть определена как

void Class::f (int value) {
    assert (value < end);
    member = value;
}

а функция, получающая входные данные из сети, может выглядеть так:

void Class::g (InMessage & msg) {
    int const value = msg.read_int();
    if (value >= end)
        throw InvalidServerData();
    f (value);
}

Это дает мне два уровня проверок.Все, где данные определяются во время выполнения, всегда вызывает исключение или немедленную обработку ошибок.Однако эта дополнительная регистрация Class::f с assert означает, что если какой-то внутренний код когда-либо вызывает Class::f, у меня еще есть проверка на здравомыслие.Мой внутренний код может не передать допустимый аргумент (поскольку я, возможно, рассчитал value из какой-то сложной серии функций), поэтому мне нравится иметь утверждение в функции настройки, чтобы документировать, что независимо от того, кто вызывает функцию, value не должно быть больше или равно end.

Кажется, это соответствует тому, что я читаю в нескольких местах: утверждения не должны быть нарушены в хорошо функционирующей программе, а исключения должны быть для исключительных и ошибочных случаев, которые все еще возможны.Поскольку теоретически я проверяю все вводимые данные, мое утверждение не должно сработать.Если да, то моя программа неверна.

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

С другой стороны, очень легко злоупотреблять assert.

int quotient(int a, int b){
    assert(b != 0);
    return a / b;
}

Правильная, правильная версия будет выглядеть примерно так:

bool quotient(int a, int b, int &result){
    if(b == 0)
        return false;

    result = a / b;
    return true;
}

Так...в долгосрочной перспективе...на большой картинке...Я должен согласиться, что assert можно злоупотреблять.Я делаю это все время.

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

Поэтому, как разработчикам языка, им следует понимать, что правильную обработку ошибок можно обеспечить даже с меньшим количеством ввода текста.Исключение утверждения, поскольку ваш механизм исключений многословен, не является решением.Ой, подождите, в Go тоже нет исключений.Очень жаль :)

Мне захотелось пнуть автора по голове, когда я это увидел.

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

Исключения также легче сочетаются с производственным кодом, что мне не нравится.Утверждение легче заметить, чем throw new Exception("Some generic msg or 'pretend i am an assert'");

Моя проблема с этими ответами, защищающими утверждение, заключается в том, что никто четко не указывает, что отличает его от обычного. фатальная ошибка, и почему утверждение не может быть подмножеством исключение.Теперь, с учетом сказанного, что, если исключение никогда не будет перехвачено?Делает ли это утверждение номенклатурой?И зачем вам когда-либо налагать в языке ограничение на то, что может быть вызвано исключение, которое /nothing/ может обработать?

Да, утверждения – это зло.

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

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

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

Иногда они служат опорой для неудачных проектов;то естьконструкция кода позволяет пользователю вызывать его так, как не следует, и утверждение «предотвращает» это.Исправьте дизайн!

Подробнее об этом я писал в своем блоге еще в 2005 году здесь: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html

Если утверждения, о которых вы говорите, означают, что программа прекращает работу, а затем существует, утверждения могут быть очень плохими.Это не значит, что они всегда использовать неправильно, это конструкция, которую очень легко использовать неправильно.У них также есть много лучших альтернатив.Подобные вещи – хорошие кандидаты на то, чтобы называться злом.

Например, сторонний модуль (да и вообще любой модуль) почти никогда не должен выходить из вызывающей программы.Это не дает вызывающему программисту никакого контроля над тем, какой риск должна принять программа в данный момент.Во многих случаях данные настолько важны, что даже сохранение поврежденных данных лучше, чем их потеря.Утверждения могут привести к потере данных.

Некоторые альтернативы утверждениям:

  • Используя отладчик,
  • Консоль/база данных/другие журналы
  • Исключения
  • Другие типы обработки ошибок

Некоторые ссылки:

Даже люди, выступающие за утверждение, думают, что их следует использовать только в целях развития и нет в производстве:

Этот человек говорит, что утверждения следует использовать, когда в модуле есть потенциально поврежденные данные, которые сохраняются после создания исключения: http://www.advogato.org/article/949.html .Это, безусловно, разумный момент, однако внешний модуль должен никогда прописать, насколько важны поврежденные данные для вызывающей программы (выходом «для» них).Правильный способ справиться с этой проблемой — создать исключение, которое дает понять, что программа теперь может находиться в несогласованном состоянии.А поскольку хорошие программы в основном состоят из модулей (с небольшим количеством связующего кода в основном исполняемом файле), утверждения почти всегда неправильны.

Не столько зло, сколько вообще контрпродуктивно.Существует разделение между постоянной проверкой ошибок и отладкой.Assert заставляет людей думать, что вся отладка должна быть постоянной, и вызывает серьезные проблемы с читаемостью при частом использовании.Обработка постоянных ошибок должна быть лучше, чем там, где это необходимо, а поскольку метод Assert вызывает свои собственные ошибки, это довольно сомнительная практика.

я никогда не использую Assert(), примеры обычно показывают что-то вроде этого:

int* ptr = new int[10];
assert(ptr);

Это плохо, я никогда так не делаю, а вдруг моя игра выделяет кучу монстров?почему я должен вылетать из игры, вместо этого вы должны корректно обрабатывать ошибки, поэтому сделайте что-то вроде:

CMonster* ptrMonsters = new CMonster[10];
if(ptrMonsters == NULL) // or u could just write if(!ptrMonsters)
{
    // we failed allocating monsters. log the error e.g. "Failed spawning 10 monsters".
}
else
{
    // initialize monsters.
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top