In Ruby, in what order do the begin, rescue, else and ensure clauses run, and what value does the whole expression return?
answer
- body first, then one branch
- else only when nothing raised
- ensure always runs, last
- ensure's value is discarded
- else errors skip sibling rescues
basics
~20 sThe body runs; on an exception the first matching rescue runs, otherwise else runs; ensure always runs last. The expression's value comes from the rescue, from else, or from the body when there is no else; ensure's value is discarded.
solid answer
~50 sRuby runs the `begin` body first. If it raises, Ruby tries the `rescue` clauses top to bottom and runs the first that matches; if nothing raised, the `else` clause runs instead. `ensure` runs last on every path: after a rescue, after `else`, when the exception matched no clause and keeps propagating, and when control leaves early through `return`, `break` or `next`. The value of the whole expression is the last value of whichever branch finished normally: the rescue body, the `else` body, or the `begin` body if there is no `else`. The `ensure` body's value is thrown away unless it uses an explicit `return`. An exception raised inside `else` is not handled by that same begin's rescue clauses, so `else` is the place for code you want protected by `ensure` but not by the rescues.
code
ruby · 17 linesinput = "42"
width = begin
puts "body"
Integer(input)
rescue ArgumentError
puts "rescue"
0
else
puts "else"
:parsed
ensure
puts "ensure"
:ignored
end
p width # => :parsed (with input = "x": prints body, rescue, ensure; width is 0)go deeper
Recall the order: body, then one of rescue or else, then ensure, and that ensure always runs.
Explain the three paths and the value rules, especially that ensure's value is dropped and that else is outside the rescue's protection.
Use else deliberately to keep the protected region small, and put all resource cleanup in ensure so early returns and unrescued errors still release it.
Review handler shape as a team convention: narrow rescue regions, success work in else, cleanup only in ensure, so failures are attributed to the right line.
## The four clauses A Ruby **exception handler** is a `begin ... end` expression with up to four parts: | Clause | Runs when | Value used? | |---|---|---| | `begin` body | always, first | yes, when there is no `else` and nothing raised | | `rescue` (one or more) | the body raised an exception the clause matches | yes, when it runs | | `else` | the body finished without raising | yes, when it runs | | `ensure` | always, last | no, unless it uses an explicit `return` | An `else` needs at least one `rescue`; Ruby rejects a lonely `else` in a `begin` block as a syntax error, since it would be pointless. ## The three paths Every run follows one of three paths: 1. **Nothing raised.** Body, then `else` (if present), then `ensure`. The value is the `else` value, or the body's value when there is no `else`. 2. **Raised and rescued.** Body up to the failing line, then the **first** matching `rescue` clause, then `ensure`. The value is the rescue body's last value. The rest of the body is skipped, and `else` does not run. 3. **Raised and not rescued.** Body up to the failing line, then `ensure`, then the exception keeps propagating to the caller. There is no value. `ensure` also runs when the body leaves early: a `return` from a method, a `break` or `next` inside a block, or a `throw`. That is what makes it the right home for closing files, sockets and locks. ## Return values in detail - The expression's value is the **last evaluated expression of the branch that completed**: rescue, `else`, or body. - The **`ensure` value is discarded**. The syntax documentation puts it plainly: without an explicit `return` in `ensure`, the block returns the last statement evaluated before entering `ensure`. - Because `begin ... end` is an expression, it can sit on the right of an assignment: `count = begin ... end`. - A method whose whole body is the handler returns the same value, since a method returns its last expression. ## Why else exists Code in `else` runs only on success, but outside the protection of the rescue clauses. That matters when the success path can itself fail in ways you do not want to mask: - If the body parses a file and the `else` clause writes the result somewhere, a failure while writing propagates instead of being reported as "could not parse". - Keeping the protected region small makes the rescue honest: it only catches errors from the lines it was written for. - `ensure` still runs after `else`, so cleanup is not affected. ## A worked example In an image-thumbnail service, parsing a width parameter might look like the snippet below. With `"42"` the output is `body`, `else`, `ensure`, and the value is `:parsed`; with `"x"` it is `body`, `rescue`, `ensure`, and the value is `0`. The `:ignored` at the end of `ensure` never becomes the result. ## Common misreadings - **"`ensure` returns its value."** Only an explicit `return` changes the result, and that has serious side effects of its own. - **"`else` runs after a rescue."** It runs only when nothing was raised. - **"All matching rescue clauses run."** Only the first match runs. - **"`ensure` is skipped when no rescue matches."** It runs before the exception leaves the block. ## How interviewers probe it Expect the question in the form of a snippet with `puts` calls in every clause, asked twice: once with input that succeeds and once with input that fails. The reliable way to answer is to trace the three paths above rather than guess: 1. Mark the line that raises, if any; everything after it in the body is skipped. 2. Find the first rescue clause whose class matches; if none does, jump straight to `ensure` and then out. 3. If nothing raised, run `else`. 4. Run `ensure` and remember that its last line does not count. 5. Name the value: the last line of whichever branch from steps 2 or 3 completed. A frequent variation adds a `return` inside the body. The answer is the same shape: `ensure` still runs, and the returned value is kept.
- If the else clause raises, does the rescue clause above it handle the error?No. The rescue clauses of a `begin` cover only its body. An exception raised in `else` propagates to the caller after `ensure` runs. That is the point of `else`: success-only code that should not be mistaken for a failure of the protected lines.
- Does ensure run when the method returns early from inside the begin body?Yes. `ensure` runs whenever control leaves the block, including `return` from a method and `break` or `next` from a block. The early return value is kept, because the `ensure` value is discarded unless `ensure` itself uses an explicit `return`.
saying these in an interview costs you the question
- The value of ensure becomes the value of the begin block
- else runs after a rescue clause has handled the error
- Every rescue clause that matches the exception runs in turn
- ensure is skipped when no rescue clause matches
- An exception raised in else is caught by the same begin's rescue