1

I've noticed that if I use the keyword return inside of a block, the entire method returns. I find it confusing that in ruby, you can optionally use the return keyword - it's like: "So what is this thing returning, if anything? Did the developer intend this method to return something?" So I like using return even inside blocks, for example:

def my_method
  items = [ 1, 2, 3 ]

  items_times_ten = items.collect { |o|
    #multiple
    #lines
    #of
    #code ...

    return o * 10  # Ruby exits the method at this point.
  }

  return items_times_ten  #This is never reached.
end

I understand why I don't need to return from the block, but if my block was more intricate, it would seem to help with clarity. My question is, why in the world would Ruby assume that I want to return control out two levels?

If my assumption is incorrect, please correct me. I just want to understand the reasons that control is handled in this way.

Josh M.
  • 26,437
  • 24
  • 119
  • 200
  • I think this answer could be of help http://stackoverflow.com/a/2325712/1856239 – Ahmad Sherif Feb 11 '14 at 20:43
  • I redact this entire question. I'm not sure why this surprised me originally - I come from a C# background and if I called `return` from within a `for` loop (a weird thing to do), the method would return! Same idea here. I think I just got annoyed trying to solve a problem and got caught up. – Josh M. Feb 11 '14 at 21:57
  • 1
    Due to some parallels that may be drawn between JavaScript and Ruby closures, this question isn't entirely off base. And calling `return` inside a C# anonymous delegate would also keep control within the method... don't knock yourself just yet! – PinnyM Feb 12 '14 at 03:23

3 Answers3

3

Herein lies one of the primary differences between a block/Proc and a lambda/Method (the other primary difference would be arity). If you don't want a call to return to exit the method, that would infer that you expect that block should be self-contained in its flow control, and be treated as an encapsulated method.

This description is essentially what a lambda is - an anonymous method. However, a standard ruby block is essentially an anonymous Proc, and takes nothing away from the flow control of the method.

As mentioned in the comments, you may be able to use next to escape the block without returning control away from the method. 'May' because next may just continue to the next item that the method iterator is passing to the block.

Related to this, see http://yehudakatz.com/2012/01/10/javascript-needs-blocks/ and Differences between Proc and Lambda

Community
  • 1
  • 1
PinnyM
  • 35,165
  • 3
  • 73
  • 81
2

If you think of a block as some kind of nested function or anonymous method, then you'd expect return to cause the block to the iterator that yielded to it. But blocks are not methods. return always causes the enclosing method to return, no matter how deeply nested within blocks it is.

steenslag
  • 79,051
  • 16
  • 138
  • 171
1

The behaviour you're seeing is explained by how Procs and Lambdas behave in ruby in the context of the return keyword.

For instance, the following give you the expected result:

def my_method
  items = [ 1, 2, 3 ]
  a = ->(o){return o * 10}  # lambda
  items_times_ten = items.collect(&a)
  return items_times_ten  #This is never reached.
end

p my_method # [10, 20, 30] 

Essentially, a return within a Proc will stop a method and return the value provided, in the other hand Lambdas will return their value to the method.

Another way around it, if you still want to use a Proc is to use implicit return. For instance:

def my_method
  items = [ 1, 2, 3 ]

  items_times_ten = items.collect { |o|
    o * 10  # Removed the explicit return
  }

  return items_times_ten  #This is never reached.
end

p my_method # [10, 20, 30] 
fmendez
  • 7,250
  • 5
  • 36
  • 35