Search code examples
rubycoding-styleguard-clause

How do I do early return in ruby to reduce nested if?


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


Solution

  • 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.