Было бы лучше использовать `throw` вместо `return false`?

Моя цель - выполнить какую-то простую функцию, но пользователь должен знать, если вызов не удался. Пример простой функции можно было увидеть здесь .

Вместо return false, я хочу использовать throw. Будет ли их влияние одинаковым на поток функции? И какой из них потребляет меньше газа?

В целом, какой из них может быть более мотивирован для использования?

Ответы (3)

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

Использование throw позволяет легко увидеть, была ли транзакция какой-либо ошибкой в ​​проводнике блокчейна.

См. также обсуждение предстоящих функций, посвященных этим https://github.com/ethereum/EIPs/issues/140 .

«Использование throw упрощает просмотр того, была ли транзакция какой-либо ошибкой в ​​проводнике блокчейна». Ирония в том, что если вам нравится делать что-то программно (т. е. без человека в цикле), то диагностировать throw СЛОЖНЕЕ, потому что все, что вы получаете, это gasUsed = gasSent, вы не получаете сообщения об ошибке и не можете понять, какая строка кода не удалась. в каком договоре.
@PaulS: транзакции также не имеют возвращаемого значения.
транзакции могут регистрировать сообщения, как я и делаю автоматизированное тестирование. (и/или написать функции доступа). Прерванная транзакция ничего не регистрирует...

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

Это очень общий вопрос. Я бы разделил ситуации на два случая:

  1. "Нет" - правильный ответ на вопрос
  2. Что-то не так с транзакцией

В первом случае return falseможет быть выход. Например, «Сегодня понедельник?» - Нет. В этом нет ничего особенно плохого, и "Нет" является правильным и ожидаемым ответом. Подразумевается, что вызывающий абонент должен быть готов к возможности того, что ответ «Нет» вернется, и обработает его соответствующим образом.

Большое количество случаев относится ко второй категории; что-то не так. В этих случаях это почти всегда предпочтительнее, на throw; мой взгляд .

Учтите, что мы можем развернуть контракт, а затем в будущем вызывающим может быть другой контракт. Если мы return false, то, по сути, возлагаем на звонящего ответственность борьбы с неожиданными результатами. Это приводит к сложности, а сложность — это то, чего мы не хотим в смарт-контрактах. С другой стороны, если мы throwпри первых признаках проблемы оказываем услугу всем будущим абонентам; если наша функция "не произошла", то ничего не произошло.

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

В смарт-контракте я склонен ошибаться рано, сильно ошибаться и ничего не объяснять. Другими словами, throwпри (почти) каждой возможности.

Общая эвристика:

  1. Проверьте ввод и выбросьте, если что-то не так.
  2. Делай вещи.
  3. Проверяйте результаты и если что-то не так, кидайте.
  4. Вернуть "успех" или результат.

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

Мы стремимся к «очевидно правильно». Минимизация сложности подразумевает, что объяснение причин является отдельной задачей (которую должны понять клиенты). Throwобычно является самым надежным и простым ответом на исключения, которые угрожают целостности системы.

Надеюсь, поможет.

Вообще, YES лучше использовать throwвместо return false.

Объективная причина заключается в том, что throwэто безопаснее, потому что оно отменяет все изменения состояния: несомненно, что в случае сбоя вызова ничего не изменится. Можно вручную отслеживать и отменять все изменения, но можно что-то упустить, сделав это «вручную».

Субъективная причина заключается в том, что правильность, безопасность и безопасность являются наиболее важными приоритетами для децентрализованных, не требующих доверия приложений (как описывает @Rob), и throwобъективно безопаснее.

Бросок против возврата описывает:

Зачем использоватьthrow

  • Любые побочные эффекты кода отменены
  • Некоторые кошельки могут предсказывать throwзаранее, предупреждая пользователя

Зачем использоватьreturn

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

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

Еще одна вещь, о которой следует помнить при использовании web3.js, заключается в том, что когда встречается Solidity throw, в настоящее время web3.js имеет проблему со falseзначениями .


В будущем

revertвероятно, будет тем, который будет использоваться, поскольку он будет иметь все преимущества throwи return:

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