Nakilon
Nakilon

Reputation: 35084

Weird `return` behaviour when switching from matz ruby to jruby

Why does this snippet:

def dump_dump
    get_dump = lambda do
        return 1 if $n
        $n = true
        module_exec &get_dump
        2
    end
    p get_dump[]
end

Module.new do
    module_exec &method(:dump_dump)
end

print 2 in ruby 2.0.0p481 (2014-05-08) [x64-mingw32]
but 1 in jruby 1.7.15 (1.9.3p392) 2014-09-03 82b5cc3 on Java HotSpot(TM) 64-Bit Server VM 1.7.0_67-b01 +jit [Windows 8-amd64]?

I would like to understand the issue.

UPD: should it be reported somewhere?

Upvotes: 4

Views: 374

Answers (2)

Max
Max

Reputation: 15965

I was always under the impression that return inside a block was undefined behavior. Can you use next instead?

For instance, Rubinius also has this problem but is much more explicit:

[1].map(&lambda { |n| return -1 })
LocalJumpError: unexpected return

Of course, using next produces the expected results:

rbx-head :003 > [1].map(&lambda { |n| next -1 })
 => [-1] 

The moral of the story is that return is defined for methods, and Procs and lambdas are not methods. next and break are the keywords to use if you wish to stop a block call.

I can't find any documentation about return's behavior from the official Ruby spec, but rubyspec does have tests that verify return causes the calling method to return.

https://github.com/rubyspec/rubyspec/blob/master/language/return_spec.rb#L184

Upvotes: 2

adzdavies
adzdavies

Reputation: 1555

A 'return' inside a lambda should return from the lambda and not return from the method. In this tricky case, it looks like jruby is not respecting the inner lambda, and instead returning all the way back to the first lambda call.

At first I thought it might be caused by a lambda call within a lambda, but now I believe it to be an issue related to block-conversion, after reducing the example down to this:

Module.new do
  test = lambda do
    return
  end
  module_exec &test
  puts 'after'
end

Here only mri prints 'after', while jruby prints nothing.

...but if we do NOT do lambda to block conversion (the &test):

Module.new do
  test = lambda do
    return
  end
  module_exec { test[] }
  puts 'after'
end

both mri and jruby print 'after'...

Upvotes: 0

Related Questions