Could someone please take 5 minutes and explain the difference between throwing errors and exceptions in Flutter/Dart. The information I find online is very contradictory and confusing.
In which cases should I throw an Error, in which cases should I throw an exception?
Which of those can or should be caught by try/catch approach?
What is the difference between throwing the difference error types, for instance an Error or an ArgumentError? Where can I find a list of these types?
Many thanks in advance!
- In which cases should I throw an Error, in which cases should I throw an exception?
Error
s are for logical errors made by the programmer. They are for conditions that should not logically occur. Throw them for preventable situations that shouldn't happen if code properly follows API contracts.
Exception
s are for runtime errors. Throw them for situations that are out of your control (such as user input, responses from servers, unexpected data when reading a file, ...).
An
Error
object represents a program failure that the programmer should have avoided.Examples include calling a function with invalid arguments, or even with the wrong number of arguments, or calling it at a time when it is not allowed.
These are not errors that a caller should expect or catch — if they occur, the program is erroneous, and terminating the program may be the safest response.
and from Effective Dart:
DO throw objects that implement
Error
only for programmatic errorsThe
Error
class is the base class for programmatic errors. When an object of that type or one of its subinterfaces likeArgumentError
is thrown, it means there is a bug in your code. When your API wants to report to a caller that it is being used incorrectly throwing anError
sends that signal clearly.Conversely, if the exception is some kind of runtime failure that doesn't indicate a bug in the code, then throwing an
Error
is misleading. Instead, throw one of the core Exception classes or some other type.
One reason why the distinction matters is that in principle, an optimizer could remove Error
conditions from the code since they should not logically occur and therefore can be considered to be dead code.
A few rules of thumb:
Would your program in principle (in the absence of other bugs) behave the same if the throw
ing code didn't exist? If so, then use an Error
.
Do you expect the thrown object to be caught? Do you expect something in the callstack to be able to recover? If so, then use an Exception
.
Now, there are some cases that could be considered gray areas. For example, a user could enter invalid input that is passed to a function that cannot handle it. Is that a logical error or a runtime error? Whose responsibility is it to check for that invalid input, the caller's or the callee's? It depends on the contract of the function. Does its API documentation state that it must not be called with certain inputs? If so, then the caller is responsible for verifying those inputs first.
- Which of those can or should be caught by try/catch approach?
You should not catch Error
s. From Effective Dart:
DON'T explicitly catch
Error
or types that implement itThis follows from the above. Since an
Error
indicates a bug in your code, it should unwind the entire callstack, halt the program, and print a stack trace so you can locate and fix the bug.Catching errors of these types breaks that process and masks the bug. Instead of adding error-handling code to deal with this exception after the fact, go back and fix the code that is causing it to be thrown in the first place.
That's also why you should avoid catch
blocks without on
clauses; such catch
blocks will catch everything.
- What is the difference between throwing the difference error types, for instance an Error or an ArgumentError? Where can I find a list of these types?
There is not going to be any exhaustive list of all Error
types since anybody can make their own custom class that implements Error
. You can see a list of the built-in Error
types by looking at the Error
documentation and looking at its "Implementers" list.