From: samuel@... Date: 2019-01-26T10:59:50+00:00 Subject: [ruby-core:91278] [Ruby trunk Feature#15567] Allow ensure to match specific situations Issue #15567 has been updated by ioquatix (Samuel Williams). @eregon - thanks for your response. > Ensure should always be executed no matter the circumstances IMHO, so I don't like to make it conditional with extra syntax. This isn't about not executing ensure, but allowing user to handle situations like "returned normally" vs "non-standard flow control". > I think the workaround using a variable set after yield (success = true) is not too bad, and clear enough. And the performance would be fine in this case (i.e. there would be no overhead if the JIT profiles the branch and only one branch is taken, like TruffleRuby). Personally, I think it's ugly, and I also think it is inefficient. I also don't think it's clearly conveying what the user is trying to do. The problem is, people write code like this: ``` begin .. ensure abnormal_path if $! end ``` This actually wrong and fails in the case of `throw` wrapped in `catch` as given in the original examples. It's actually not clear from the code if this is what the user wanted or not. Because you can't express clearly what you are actually interested in. There should be no overhead when exiting normally in the following situation: ``` begin yield ensure when not return return :abnormal end ``` As soon as you write code like: ``` begin yield success = true ensure return :abnormal unless success end ``` you guarantee that the code must pass through the ensure block. But your intention is, only execute this code if the code didn't return normally (or some other flow control). So, I don't think the argument about always executing ensure holds up - this isn't about changing the semantics of `ensure` but extending it to handle explicitly what people are already doing, albeit probably incorrectly and inefficiently. ---------------------------------------- Feature #15567: Allow ensure to match specific situations https://bugs.ruby-lang.org/issues/15567#change-76524 * Author: ioquatix (Samuel Williams) * Status: Open * Priority: Normal * Assignee: ioquatix (Samuel Williams) * Target version: 2.7 ---------------------------------------- There are some situations where `rescue Exception` or `ensure` are not sufficient to correctly, efficiently and easily handle abnormal flow control. Take the following program for example: ``` def doot yield ensure # Did the function run to completion? return "abnormal" if $! end puts doot{throw :foo} puts doot{raise "Boom"} puts doot{"Hello World"} catch(:foo) do puts doot{throw :foo} end ``` Using `rescue Exception` is not sufficient as it is not invoked by `throw`. Using `ensure` is inefficient because it's triggered every time, even though exceptional case might never happen or happen very infrequently. I propose some way to limit the scope of the ensure block: ``` def doot yield ensure when raise, throw return "abnormal" end ``` The scope should be one (or more) of `raise`, `throw`, `return`, `next`, `break`, `redo`, `retry` (everything in `enum ruby_tag_type` except all except for `RUBY_TAG_FATAL`). Additionally, it might be nice to support the inverted pattern, i.e. ``` def doot yield ensure when not return return "abnormal" end ``` Inverted patterns allow user to specify the behaviour without having problems if future scopes are introduced. `return` in this case matches both explicit and implicit. -- https://bugs.ruby-lang.org/ Unsubscribe: