summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2023-05-26 23:58:40 +0900
committerGitHub <noreply@github.com>2023-05-26 07:58:40 -0700
commit618a04d211147ed5d95f724964e1f63c309834cf (patch)
tree6edddf443f839857cb3a7dd72170d2384b4371b6 /doc
parent4847b7ac28d744dcf6caedd71dc5a7be21d4187c (diff)
Document throw/catch in the control expressions document [ci skip]
This are implemented as Kernel methods and not keywords, but I still think they are worth documenting with the other control flow expressions.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/7856 Merged-By: jeremyevans <code@jeremyevans.net>
Diffstat (limited to 'doc')
-rw-r--r--doc/syntax/control_expressions.rdoc68
1 files changed, 68 insertions, 0 deletions
diff --git a/doc/syntax/control_expressions.rdoc b/doc/syntax/control_expressions.rdoc
index df3b5ced38..5350585f15 100644
--- a/doc/syntax/control_expressions.rdoc
+++ b/doc/syntax/control_expressions.rdoc
@@ -569,3 +569,71 @@ evaluated on the following iteration:
Here, the flip-flop turns on when +value+ equals 2, but doesn't turn off on the
same iteration. The `off' condition isn't evaluated until the following
iteration and +value+ will never be two again.
+
+== throw/catch
+
++throw+ and +catch+ are used to implement non-local control flow in Ruby. They
+operate similarly to exceptions, allowing control to pass directly from the
+place where +throw+ is called to the place where the matching +catch+ is
+called. The main difference between +throw+/+catch+ and the use of exceptions
+is that +throw+/+catch+ are designed for expected non-local control flow,
+while exceptions are designed for exceptional control flow situations, such
+as handling unexpected errors.
+
+When using +throw+, you provide 1-2 arguments. The first argument is the
+value for the matching +catch+. The second argument is optional (defaults to
++nil+), and will be the value that +catch+ returns if there is a matching
++throw+ inside the +catch+ block. If no matching +throw+ method is called
+inside a +catch+ block, the +catch+ method returns the return value of the
+block passed to it.
+
+ def a(n)
+ throw :d, :a if n == 0
+ b(n)
+ end
+
+ def b(n)
+ throw :d, :b if n == 1
+ c(n)
+ end
+
+ def c(n)
+ throw :d if n == 2
+ end
+
+ 4.times.map do |i|
+ catch(:d) do
+ a(i)
+ :default
+ end
+ end
+ # => [:a, :b, nil, :default]
+
+If the first argument you pass to +throw+ is not handled by a matching
++catch+, an UncaughtThrowError exception will be raised. This is because
++throw+/+catch+ should only be used for expected control flow changes, so
+using a value that is not already expected is an error.
+
++throw+/+catch+ are implemented as Kernel methods (Kernel#throw and
+Kernel#catch), not as keywords. So they are not usable directly if you are
+in a BasicObject context. You can use Kernel.throw and Kernel.catch in
+this case:
+
+ BasicObject.new.instance_exec do
+ def a
+ b
+ end
+
+ def b
+ c
+ end
+
+ def c
+ ::Kernel.throw :d, :e
+ end
+
+ result = ::Kernel.catch(:d) do
+ a
+ end
+ result # => :e
+ end