3

Coming from Python, I really miss the else clause in the try-except chains in Dart.

What's the most idiomatic to simulate the else clause in Dart?


Here is an example that benefits from an else block.

This:

var didFail = false;
try {
    startDownload()
} catch (e) {
    didFail = true;
    downloadFailed()
}   
if (!didFail) {
    downloadSuccess()
}    
afterDownload()

vs:

try {
    startDownload()
} catch (e) {
    downloadFailed()
} else {
    downloadSuccess()    
}
afterDownload()
Dev Aggarwal
  • 7,627
  • 3
  • 38
  • 50
  • 3
    Put the code from `else` at the end of the `try` block, before the `catch`? Not 100% the same, but maybe close enough. – tobias_k Feb 22 '19 at 14:37
  • I'm confused as to why you need the `else` – SuperStew Feb 22 '19 at 14:38
  • https://stackoverflow.com/questions/855759/python-try-else I always find myself needing one. – Dev Aggarwal Feb 22 '19 at 14:39
  • 1
    There are certainly some cases where it's nice to have it, but _need_ it? Probably not. Can you show some example? – tobias_k Feb 22 '19 at 14:40
  • 1
    This question isn't a discussion of whether this should be included in the language. I ask what's the most idiomatic way to handle the case when a try-catch block might just use a `return` statement, and never execute the code after it. – Dev Aggarwal Feb 22 '19 at 14:43
  • There's also the obvious case where some piece of code only gets executed when absolutely no exception occurs, which is the main reason for having an `else` block. – Dev Aggarwal Feb 22 '19 at 14:46
  • 1
    I did not want to discuss that, but it will be much easier to provide an answer if we have an example of the `try/catch/else` code you want to replicate. For example, I see no problem of putting that "absolutely no exception occurred" code at the very end of the `try` block. – tobias_k Feb 22 '19 at 14:50
  • Does the `try / on exception catch / on exception catch / catch` syntax not work for you? See the catch section of the Language Tour https://www.dartlang.org/guides/language/language-tour#catch – jessi Feb 22 '19 at 14:55
  • @jessi `catch` catches any exceptions not already caught previously; Python's `else` clause executes if there *were* no exceptions. – chepner Feb 22 '19 at 14:57
  • @tobias_k Done. – Dev Aggarwal Feb 22 '19 at 15:00
  • @DevAggarwal The example should probably include a `finally` block as well, to capture precisely when the `else` clause gets executed. (Or, just see my answer.) – chepner Feb 22 '19 at 15:02

2 Answers2

2

Full disclosure: my entire experience with Dart is the 2 minutes I just spent reviewing the syntax of its try statement. This is based solely on a observation about Python's semantics.


What does else do in Python?

Skip to the end of the answer for the suggested Dart code.

The following two pieces of code are very similar in Python:

try:
    ...
    <last code>
except SomeError:
    ...
finally:
    ...

and

try:
    ...
except SomeError:
    ...
else:
    <last code>
finally:
    ...

<last code> will be executed in the same circumstances in both. The difference is that any exceptions raised by <last statement> will be caught in the first, but not in the second.

Simulating else in Python

To simulate else's semantics in Python, you would use an additional try statement and a flag to indicate if an exception should be rethrown.

else_exception = False
try:
    ...
    try:
        <last code>
    except Exception as e:
        else_exception = True
except SomeError:
    ...
finally:
    if else_exception:
        raise e
    ...

We check if the nested try caught an exception in the finally clause, since the else clause would have executed before anything else in finally. If there was an exception, reraise it now that it won't be immediately caught, just as in the else. Then you can proceed with the rest of finally.


Simulating else in Dart

As far as I can tell, the same logic would be necessary in Dart.

bool else_exception = false;
try {
  ...
  try {
     <last code>
  } catch (e) {
    else_exception = true;
  }
} on SomeError catch (e) {
  ...
} finally {
  if (else_exception) {
    throw e;
  }
  ...
}

Note that if <last code> throws an exception, the above code will not properly preserve the stacktrace. To do that, some more care would be needed:

bool else_exception = false;
try {
  ...
  try {
     <last code>
  } catch (e) {
    else_exception = true;
    rethrow;
  }
} on SomeError catch (e) {
  if (else_exception) {
     rethrow;
  }
  ...
}
jamesdlin
  • 81,374
  • 13
  • 159
  • 204
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 1
    (Posting as a community wiki because I fully expect my Dart code to need some correction.) – chepner Feb 22 '19 at 15:03
  • Nice one. I guess in my case I didn't need those two nested blocks, since I wanted to catch _all_ exceptions. – Dev Aggarwal Feb 22 '19 at 15:11
  • Note that in Python, there is a difference between `except` and `except Exception`. Usually, you just use `except Exception` and handling things like `SystemExit` explicitly, if at all. I don't know if Dart has anything similar in its exception handling code. – chepner Feb 22 '19 at 15:11
  • @chepner The original transformation you gave won't preserve the stacktrace from exceptions thrown by the `else` block. I've modified your answer to try to deal with that, although it's significantly uglier (one reason is that `rethrow` is valid only within a `catch` block). – jamesdlin Mar 01 '19 at 00:51
-1

In most cases, you should just be able to write whatever goes into the else block directly at the very end of the try block. There may be some cases where the else block is useful, and can provide somewhat clearer or more expressive code, but you can, e.g., write your code much more compact than what you did in your "no else" example as

try {
    start_download() // exception?
    // yay, no exception
    download_success()
} catch (e) { // preferrably "on KindOfException catch (e)"
    download_failed()        
}
afterDownload()

It may not be as explicit that download_success only executes if there was no exception, but implicitly it is clear, because if there were an exception, the try block would have been aborted and the execution would have gone into the catch block.

Of course, this also means that exceptions raised in download_success() will also go to the catch block. This may be prevented by using a more specific exception, e.g. on VeryBadDownloadException catch (e), assuming that start_download and download_success will not raise exactly the same kinds of exceptions.

tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • 1
    That `catch` block will catch any exceptions occurred in the `download_success()` routine. That can lead to a lot of unintended behaviour. It's not a matter of explicitness, but code correctness. – Dev Aggarwal Feb 22 '19 at 15:07
  • 1
    This would also run `download_failed` even if the download succeeded, but `download_success` raised an exception. I know you said this would work in *most* cases, but I think it's important to point out when it would *not*. – chepner Feb 22 '19 at 15:07
  • @DevAggarwal See, that's why I wanted to have an example, preferrably with some description _why_ you think an `else` would help there. From just the code and your comments, it did not seem like `download_success` would raise an exceptoin, much less the same kind of exception as `start_download` would. – tobias_k Feb 22 '19 at 15:09