1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Kernel#loop returns the result value of a finished iterator

* vm_eval.c (rb_f_loop): When a loop is stopped by a StopIteration
  exception, return what the enumerator has returned instead of
  nil. [ruby-core:71133] [Feature #11498]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52218 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
knu 2015-10-22 09:58:01 +00:00
parent 53d6e605b0
commit f4c487173c
4 changed files with 40 additions and 4 deletions

View file

@ -1,3 +1,9 @@
Thu Oct 22 18:52:53 2015 Akinori MUSHA <knu@iDaemons.org>
* vm_eval.c (rb_f_loop): When a loop is stopped by a StopIteration
exception, return what the enumerator has returned instead of
nil. [ruby-core:71133] [Feature #11498]
Thu Oct 22 18:25:10 2015 Shugo Maeda <shugo@ruby-lang.org>
* lib/net/imap (idle): add a new argument timeout for keep-alive.

5
NEWS
View file

@ -64,6 +64,11 @@ with all sufficient information, see the ChangeLog file.
this parameter is bitwise-ORed to oflags generated by normal mode argument.
[Feature #11253]
* Kernel
* Kernel#loop, when stopped by a StopIteration exception, returns
what the enumerator has returned instead of nil.
* Module
* Module#deprecate_constant [Feature #11398]

View file

@ -46,6 +46,14 @@ class TestEnumerator < Test::Unit::TestCase
}
end
def test_loop_return_value
assert_equal nil, loop { break }
assert_equal 42, loop { break 42 }
e = Enumerator.new { |y| y << 1; y << 2; :stopped }
assert_equal :stopped, loop { e.next while true }
end
def test_nested_iteration
def (o = Object.new).each
yield :ok1

View file

@ -24,7 +24,7 @@ static void vm_set_eval_stack(rb_thread_t * th, const rb_iseq_t *iseq, const rb_
static int vm_collect_local_variables_in_heap(rb_thread_t *th, const VALUE *dfp, const struct local_var_list *vars);
static VALUE rb_eUncaughtThrow;
static ID id_tag, id_value;
static ID id_result, id_tag, id_value;
#define id_mesg idMesg
/* vm_backtrace.c */
@ -1078,6 +1078,12 @@ loop_i(void)
return Qnil;
}
static VALUE
loop_stop(VALUE dummy, VALUE exc)
{
return rb_attr_get(exc, id_result);
}
static VALUE
rb_f_loop_size(VALUE self, VALUE args, VALUE eobj)
{
@ -1100,15 +1106,25 @@ rb_f_loop_size(VALUE self, VALUE args, VALUE eobj)
* # ...
* end
*
* StopIteration raised in the block breaks the loop.
* StopIteration raised in the block breaks the loop. In this case,
* loop returns the "result" value stored in the exception.
*
* enum = Enumerator.new { |y|
* y << "one"
* y << "two"
* :ok
* }
*
* result = loop {
* puts enum.next
* } #=> :ok
*/
static VALUE
rb_f_loop(VALUE self)
{
RETURN_SIZED_ENUMERATOR(self, 0, 0, rb_f_loop_size);
rb_rescue2(loop_i, (VALUE)0, 0, 0, rb_eStopIteration, (VALUE)0);
return Qnil; /* dummy */
return rb_rescue2(loop_i, (VALUE)0, loop_stop, (VALUE)0, rb_eStopIteration, (VALUE)0);
}
#if VMDEBUG
@ -2184,6 +2200,7 @@ Init_vm_eval(void)
rb_define_method(rb_eUncaughtThrow, "value", uncaught_throw_value, 0);
rb_define_method(rb_eUncaughtThrow, "to_s", uncaught_throw_to_s, 0);
id_result = rb_intern_const("result");
id_tag = rb_intern_const("tag");
id_value = rb_intern_const("value");
}