throw, try, catch pattern

I would need confirmation that this pattern of nested throw is not correct when using do try. In fact, when I catch a thrown error, I want in some cases to throw a new exception.


These patterns seem to the cause of some problems (now corrected).


do {
     let result = try someFunction
} catch someError {
     throw someException     // A new one
} catch {
     // generic actions
}


Or with nested do

do {
     let firstResult = try someFirstFunction
     do {          // A nested do pattern
          let result = try someFunction
     } catch someError {
          throw someFirstException     // A new one
     } catch {
          // generic actions
     }
     } catch someFirstException {
         
     } catch {
          // generic actions
     }
}

Am I right assuming that in such a pattern, someException may be trapped either at line 6 or 10 ?

Answered by QuinceyMorris in 291206022

Yes, it's clear. No, it won't be caught on line 5.


You can think of this as the 2nd throw jumping out of the try-catch construct. You can also regard the original throw as having been caught already. Or, you can think of the catch blocks catching only throws inside the "do" scope.


Note that there's no real point for it to work the way you suggest, because you already know what kind of error it is, so there's no need to re-analyze it. Further, if it was a kind of "re-catch", any errors thrown inside the final default catch block would produce an infinite recursion.

It's a little unclear what you're asking. What is it that's "not correct"? Also, there seems to be a formatting (indenting) error in your second fragment which confuses the issue.


In your second fragment:


— if "someFunction" throws "someFirstException", it will be caught at line 7 (unless it's some kind of "someError", in which case it will be caught at line 5).


— if "someFunction" throws "someError", the "someFirstException" error that's thrown at line 6 will be caught at line 10, not line 7 (or 5).

Sorry for the poor explanation.


The question on the first example is :

will someException thrown at line 04 be handled by the catch of line 05 (that's what I believe but am not sure)? Or is the set of catch associated with the do be considered as a block and the throw someException jumps out of the block, after line 07.


Hope question is clear ?

Accepted Answer

Yes, it's clear. No, it won't be caught on line 5.


You can think of this as the 2nd throw jumping out of the try-catch construct. You can also regard the original throw as having been caught already. Or, you can think of the catch blocks catching only throws inside the "do" scope.


Note that there's no real point for it to work the way you suggest, because you already know what kind of error it is, so there's no need to re-analyze it. Further, if it was a kind of "re-catch", any errors thrown inside the final default catch block would produce an infinite recursion.

Thanks.


So, the wrong behavior I had came from another cause !


However, in the second example, where a do catch is nested in another, wouldn't it occur ?

do {
     let firstResult = try someFirstFunction
     do {          // A nested do pattern
          let result = try someFunction
     } catch someError {
          throw someFirstException     // A new one
     } catch {
          // generic actions
     }
     // End of nested do … catch

     // trap exceptions for the highest level do … catch
     } catch someFirstException {
       
     } catch {
          // generic actions
     }


More precisely, would the throw someFirstException at line 6 be trapped at line 13 ? Or does the throw throws me out of the throwing function as a whole ?

Yes, if you throw it at line 6, it'll be caught at line 13.


The rule is that the error will be caught at the first (nearest enclosing) do-catch scope. In this case, that's within the current function. In other cases, it can "return" through several function calls until it's caught.

Thanks, hence the problems I had.


I did thought at the beginning that throw forces an exit from the func, hence the use of defer.

I will re read Swift book to see how it is explained.

And take another look at indentation. I know this is the common indentation style. But the indentation is for you to read the code. You don't have to comply with how everyone else write code just becaue they do it that way.


do
  {
  let firstResult = try someFirstFunction

  do
    {
    // A nested do pattern
    let result = try someFunction
    }
  catch someError
    {
    // A new one
    throw someFirstException
    }
  catch
    {
    // generic exceptions
    }
  }
catch someFirstException
  {
  }
catch
  {
  // generic exceptions
  }

This way, you don't need comments to tell you where code sections begin and end. The indentation tells you that.


In Swift, defer is just a backwards way of writing a "finally" clause.


Exceptions have caused a lot of problems for a lot of people over the years. I advise very limited use of them. They are very powerful when used correctly, but I rarely see that. They should only be used to detect an error and restore your program to a stable state. Do not use them for flow control. That seems to be the path you are headed down.

Thanks for advice on identation ; the comments were not for me but just for publishing and making it less boring to read !


I disagree on your point on avoiding exceptions ; it is not used for flow control here. Clearly, one could have a return code for the function that tells what was the cause of error and handle this in a switch at the caller's point.

But, aren't exceptions exactly done for this ? If you look at the examples in Swift doc, they use exceptions to report printer error (paper out, …).

Not possible to call a throw from within a catch body directly until another do try catch has been declared inside said catch body.

throw, try, catch pattern
 
 
Q