1

I always use early return to reduce nested if (programing with other languages) like this:

//instead of nested if like this 
if (condition1) {
   if (condition2) {
     if (condition3) {
       //... finally meet all conditions 
       //do the job 
     }
   }
}
//return early 
if (!condition1) {
   //clean up resource in step #1
   return
}
if (!condition2) {
   //clean up resource in step #2
   return
}
if (!condition3) {
   //clean up resource in step #2
   return
}
...
//finally meet all conditions 

But how do I do early return in ruby ? I can't return inside a if block in Ruby. I got the error

"Uncaught exception: unexpected return ... `block (2 levels) in ': unexpected return (LocalJumpError)"

---- update -----

Sorry I forgot to mention that in a simple situation like this it works

def early(input)
  if (input <0)
    puts 'should >0'
    return
  end
  puts 'good'
end

I am learning Fiber and I use the sample from https://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/

def http_get(url)
  f = Fiber.current
  http = EventMachine::HttpRequest.new(url).get

  # resume fiber once http call is done
  http.callback { f.resume(1,http) }
  http.errback  { f.resume(2,http) }

  return Fiber.yield
end

EventMachine.run do
  Fiber.new{
    result = http_get('https://www.google.com/')
    if (result[0] ==2) 
      puts "error"
      return # using break has the error 'break from proc-closure (LocalJumpError)' too 
    end
    result = http_get('https://www.google.com/search?q=eventmachine')
    page = result[1]
    puts "Fetched page 2: #{page.response}"
  }.resume
end

I got the error.

---- update 2 ----

With the answer and comments I got I found this How can I return something early from a block?

And this Why does the break statement in ruby behave differently when using Proc.new v. the ampersand sign? explains why break won't work either. To quote "break is supposed to make the caller of the block return, but Proc.new has already returned."

return vs break vs next is definitely an obstacle for a ruby newbie

Qiulang
  • 10,295
  • 11
  • 80
  • 129

1 Answers1

0

Use next instead of return to exit the inner block early:

class A 
  def run 
    yield
  end
end

a = A.new
a.run do 
  puts "ola"
  next
  puts "hello"
end

In order to use return, it should be place directly inside the method body.

But when you pass a block to a function, with EventMachine.run do and with Fiber.new{, the code inside the block is not directly inside the function, but is passed as a parameter to a different function. Inside the block you cannot call return, but you can exit early with next.

I am guessing that the reason the designers decided this is because a return inside the block would cause confusion whether the block would return, or the entire method.

user000001
  • 32,226
  • 12
  • 81
  • 108
  • Eh. How is it different from my answer? – Aleksei Matiushkin Dec 21 '18 at 09:45
  • @AlekseiMatiushkin: Unless I misunderstood, your answer suggests to use `tap`, mine doesn't – user000001 Dec 21 '18 at 09:47
  • It does not matter whether it’s `tap`, `run`, or `foo`; what matters, blocks in ruby can be interrupted with `break`. Anyway. – Aleksei Matiushkin Dec 21 '18 at 09:48
  • Hi can you explain why break works while return not and when should I use one over the other ? I am learning ruby now and some grammars always seem odd to me (coming from C). Thanks! – Qiulang Dec 21 '18 at 09:49
  • BTW in my example break didn't work either, I got Uncaught exception: break from proc-closure /Users/qiulang/Dev/ruby-HW/fiber.rb:65:in `block (2 levels) in ' /Users/qiulang/Dev/ruby-HW/fiber.rb:65:in `block (2 levels) in ': break from proc-closure (LocalJumpError) – Qiulang Dec 21 '18 at 09:52
  • @AlekseiMatiushkin break didn't work for my example either. – Qiulang Dec 21 '18 at 09:53
  • 2
    @Qiulang: `return` returns from the closest lexically enclosing method body. There is no method body enclosing your `return`. `next` returns from the closest lexically enclosing block. `break` returns from the closest lexically enclosing block and signals the method `yield`ing to the block to stop iterating. So, if you use a method like `Enumerable#each`, for example, `next` will skip to the next iteration and `break` will abort the iteration altogether. – Jörg W Mittag Dec 21 '18 at 10:07
  • So the reason I can't use break here is because there is no method wrap my Fiber.new block ? – Qiulang Dec 21 '18 at 10:12