Hunter McMillen
Hunter McMillen

Reputation: 61540

Ruby: best way to handle multiple returns

I am working in Ruby and have found the need to have conditional return statements at the end of some/most of my methods.

Here is what I have:

 # <ident-list> -> [ident] <ident-A>
  def ident_list(keys)
    id = nil
    ident_a_node = nil

    ## method hidden

    return IdentifierListNode.new(id, ident_a_node) unless id.nil?
    return nil
  end

Is there a better/cleaner way to go about this with multiple returns?

Upvotes: 1

Views: 7171

Answers (5)

Matt Sanders
Matt Sanders

Reputation: 10825

While you can make the return into a one-liner, this is one of those cases where I think being a little more explicit and skimmable is worth it. This is certainly debatable, but after working on a lot of different teams with rubyists I think that if you go to a one line version there's a good chance that you (or another member of your team) may misinterpret the potential exit states when looking at the code later.

I'd suggest:

def ident_list(keys)
  id = nil
  ident_a_node = nil

  ## method hidden

  return IdentifierListNode.new(id, ident_a_node) unless id.nil?
  nil
end

or if you don't care about the difference between false/nil in this case, even better:

def ident_list(keys)
  id = nil
  ident_a_node = nil

  ## method hidden

  return IdentifierListNode.new(id, ident_a_node) if id
  nil
end

Pretty much everyone who has worked in ruby for a little while understands that a final value in a method is implicitly returned (i.e. this will return nil if no return beforehand) - and you can get that a glance. My experience is that lots of people who have worked with ruby for a long time still get tripped up by variations of && and less explicit conditional returns.

If you are determined to use a one-liner, I'd go with Ryan's answer, mostly because it's an idiom that is used fairly commonly and less likely to be confused:

def ident_list(keys)
  id = nil
  ident_a_node = nil

  ## method hidden

  id && IdentifierListNode.new(id, ident_a_node)
end

One caveat to this approach is that you actually end up with three possible return states instead of two (the other options will only return nil or your new IdentifierListNode):

  • When id is nil, return value will be nil
  • When id is false, return value will be false
  • When id is anything else, return value will be your IdentifierListNode object

Upvotes: 1

Ryan Townsend
Ryan Townsend

Reputation: 1015

When using double ampersands (AND operator), the right hand will return, so you can shorten Adrian's answer to this:

return (id && IdentifierListNode.new(id, ident_a_node))

As the first part of the condition checks for the presence of id, nil will be returned. If this is the last statement, you can remove the "return" altogether since it's implied in Ruby, leaving just:

id && IdentifierListNode.new(id, ident_a_node)

Tested with the following:

def check(input)
  input && input * 2
end

check(nil) # => nil
check(123) # => 246

Upvotes: 3

Filipe Miguel Fonseca
Filipe Miguel Fonseca

Reputation: 6436

As it stands you'll be making nil checks all over the place.

I know this might not answer directly your question, but you should try to adopt other coding pattern. Worth reading:

How to avoid “!= null” statements in Java?

Upvotes: 0

Dave Isaacs
Dave Isaacs

Reputation: 4539

The last line before the end can be simply

IdentifierListNode.new(id, ident_a_node) unless id.nil?

The last statement executed is the return value of the method. If id is nil, the statement will evaluate as nil, and if not then the new IdentifierListNode instance will be returned.

Upvotes: 5

Adrian Serafin
Adrian Serafin

Reputation: 7725

return (id.nil? ? nil : IdentifierListNode.new(id, ident_a_node))

Upvotes: 1

Related Questions