Throw Instead of Returning a Success Bool
PublicReport a failed operation by throwing, not by returning false, so the error and its stack trace reach the caller and cannot be silently ignored.
Throw Instead of Returning a Success Bool
Report a failed operation by throwing, not by returning false, so the error and its stack trace reach the caller and cannot be silently ignored.
An operation that can fail reports failure by throwing.
A bool or Future<bool> return value answers a domain question ("is it valid?", "does the user have access?"), never "did it work?".
Return void (or the operation's actual result) and let failures propagate as exceptions.
Why?
- The error is gone.
By the time
falsereaches the caller, the exception, its message and its stack trace have been thrown away. The caller knows something failed but can no longer log it, report it, or branch on the cause. falseis silently ignorable. An unchecked bool return compiles without complaint, so a forgotten check ships. An unhandled exception surfaces in logs and crash reporting.- The caller needs try-catch anyway.
A
Futurecan always fail (TimeoutException,SocketException). Funneling some failures into a bool splits error handling into two channels that both must be checked. The same reasoning rejectsFuture<Either<T>>, see prefer exceptions.
Bad
Future<bool> removeBackpack(BackpackUuid uuid) async {
try {
await api.removeBackpack(backpackId: uuid);
return true;
} catch (e, stack) {
log('Error removing backpack', error: e, stackTrace: stack);
return false;
}
}
final success = await controller.removeBackpack(uuid);
if (!success) {
showErrorToast(); // failed, but nobody can say why
}
Good
Future<void> removeBackpack(BackpackUuid uuid) async {
try {
await api.removeBackpack(backpackId: uuid);
} catch (e, stack) {
log('Error removing backpack', error: e, stackTrace: stack);
rethrow;
}
}
try {
await controller.removeBackpack(uuid);
} catch (_) {
// Logged with its stack trace by the controller.
showErrorToast();
}
Exceptions
A bool that answers a real question about the domain is fine:
bool isValidEmail(String input) { /* ... */ }
Future<bool> hasNetworkConnection() { /* ... */ }
A dialog result like showDialog<bool> is an answer ("did the user confirm?"), not a success flag.
For expected, non-exceptional absence prefer a nullable result over a bool: int.tryParse returns null, it does not return false.