0

I am currently using begin/rescue/end + exceptions to implement flow control. So, here is where I am capturing the exceptions and doing different things based on which exception is raised:

collection.each do |object|
  begin
    process_object(object)
  rescue SkippedRowError
    # handle skipped row
    next
  rescue AbortedRowError
    # handle aborted row
    next
  end
end

Because each object from the collection is being processed in a method (process_object, which calls a bunch of other methods and results in a deeply nested call stack), I can't use next to stop execution and proceed to the next object in the collection. Hence my use of exceptions.

However, I have read that exceptions should not be used for flow control and that, instead, I should use catch/throw. So, my question:

Is there a way to accomplish the same thing with catch/throw?


My conclusion so far: As far as I can tell, catch/throw is really only useful if you have a single scenario where you want to return early. If you have more than one scenario where you want to return early, then it looks like you have to nest catch blocks inside of each other. But in that case, you really only get access to a return value; there doesn't seem to be a good way to execute code.

Richard Jones
  • 4,760
  • 3
  • 27
  • 34
  • 4
    I'm unclear what the motivation is. How would using `try`...`catch` potentially be cleaner code? There are other ways you *could* structure the code, which *might* be better (I'd need to know more context to advise), but I don't see what difference `try/catch` could make over `begin/end`. – Tom Lord Apr 08 '18 at 23:01
  • 2
    might help if you showed a more complete example; this lacks context. – max pleaner Apr 08 '18 at 23:40
  • 1
    Currently, you are using exceptions to control flow, not “`begin`/`do`/`end`” (FYI: `begin`/`end` cannot control flow by any mean.) And using exceptions to control flow is considered to be a bad practice. Use normal standard iterator with early `next`/`break` instead. – Aleksei Matiushkin Apr 09 '18 at 05:00
  • @TomLord It is common advice that rescuing exceptions should not be used for control flow and that `catch/throw` should be used instead. I added more context to the question, which hopefully makes my use case more clear, but as far as I can tell, `catch/throw` won't work for what I'm trying to do. I'm just trying to verify that that is the case. – Richard Jones Apr 09 '18 at 13:02
  • @mudasobwa Sorry, that should have been `begin/rescue/end`, not `begin/do/end`. I fixed that in an edit. I have read that using exceptions for control flow is considered bad practice, but I have a deeply nested call stack which eliminates `next/break` as control flow options. The other common suggestion is to use `catch/throw`, but that only seems to work well when you have a _single_ scenario in which you want to stop execution early. If you have more than one scenario where this is the case, you have to nest `catch` blocks, which seems like a mess. – Richard Jones Apr 09 '18 at 13:08
  • @RichardJones It's also common advice that `throw/catch` should be avoided unless absolutely necessary - since it's basically the same as [writing a `GOTO` statement](https://stackoverflow.com/questions/3517726/what-is-wrong-with-using-goto)! I think your use of exceptions here is OK since it would qualify as "abandoning execution after something went wrong", but as I say there *are* other ways this could be written - but I really cannot give concrete advice without more context! – Tom Lord Apr 09 '18 at 13:54
  • 2
    In particular, you may be better off skipping execution in a **guard clause** rather than via exceptions. – Tom Lord Apr 09 '18 at 13:56
  • you could abstract your error handling into a new method and deal with it there as well. just pass the error and the object to the method handle what you want to and reraise the rest – engineersmnky Apr 09 '18 at 16:45

0 Answers0