Бросок против возврата

Мы пытаемся выяснить, следует ли использовать returnили throwв Solidity, когда условие не выполняется, и мы не предполагаем злонамеренность пользователя. Вот плюсы и минусы, которые мы выяснили до сих пор:

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

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

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

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

Есть ли какие-либо другие соображения (или лучшие практики), о которых нам следует знать?

Ответы (2)

Все соображения в вопросе полезны.

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

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

РЕДАКТИРОВАТЬ: поскольку throwпотребляет весь газ, следите за инструкцией EIP 140 REVERT :

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

Будьте осторожны, потому что throw не возвращает отправленный эфир, а только откатывает изменения в данных.
@PabloYabo Исправьте, если вы имели в виду, что throwпотребляет весь газ, и я добавил уточнение. В противном случае передача эфира отменяется с помощью a throwили revert.
Я имею в виду, что если вы вызываете функцию отправки для отправки эфира на другую учетную запись, она не откатывает отправленный эфир с помощью броска.
@PabloYabo A throwоткатывает отправленный эфир: ethereum.stackexchange.com/questions/2428/…
бросок возвращает отправленный эфир, но не откатывает эфир, отправленный внутри исполняемого кода. Он возвращает msg.value, но только это. Если вы отправили эфир в своем коде, он не откатывается.
Привет @PabloYabo Не могли бы вы открыть новый вопрос о том, что вы наблюдаете? Выбрасываемая транзакция потребляет весь газ, а все изменения состояния и передачи эфира откатываются.

Вы также можете подумать, почему бы не использовать throw/return:

Почему бы не использовать бросок

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

Почему бы не использовать возврат

  • Вызывающим сторонам может быть непонятно, что функция может быть запущена неудачно. Это может привести к катастрофическим последствиям: contract.doSomeImportantCall()может return false, но вместо этого вызывающий объект ожидал бросок, поэтому, если он не проверит возвращаемое значение, могут произойти плохие вещи.
  • Вы обнаружите, что везде возвращаете несколько значений: например, returns (bool _successful, uint _result)vsreturns (uint _result)
  • Обработка нескольких возвращаемых значений обременительна и трудна для тестирования.

На самом деле нет единого мнения о том, как правильно поступать. На самом деле, в настоящее время ведутся дебаты по поводу броска или возврата transfer, например, для обновленной спецификации ERC20.