Is It Good Style to Explicitly Return in Ruby

Is it good style to explicitly return in Ruby?

Old (and "answered") question, but I'll toss in my two cents as an answer.

TL;DR - You don't have to, but it can make your code a lot more clear in some cases.

Though not using an explicit return may be "the Ruby way", it's confusing to programmers working with unfamiliar code, or unfamiliar with this feature of Ruby.

It's a somewhat contrived example, but imagine having a little function like this, which adds one to the number passed, and assigns it to an instance variable.

def plus_one_to_y(x)
@y = x + 1
end

Was this meant to be a function that returned a value, or not? It's really hard to say what the developer meant, as it both assigns the instance variable, AND returns the value assigned as well.

Suppose much later, another programmer (perhaps not that familiar with how Ruby does returns based on last line of code executed) comes along and wants to put in some print statements for logging, and the function becomes this...

def plus_one_to_y(x)
@y = x + 1
puts "In plus_one_to_y"
end

Now the function is broken if anything expects a returned value. If nothing expects a returned value, it's fine. Clearly if somewhere further down the code chain, something calling this is expecting a returned value, it's going to fail as it's not getting back what it expects.

The real question now is this: did anything really expect a returned value? Did this break something or not? Will it break something in the future? Who knows! Only a full code review of all calls will let you know.

So for me at least, the best practice approach is to either be very explicit that you are returning something if it matters, or return nothing at all when it doesn't.

So in the case of our little demo function, assuming we wanted it to return a value, it would be written like this...

def plus_one_to_y(x)
@y = x + 1
puts "In plus_one_to_y"
return @y
end

And it would be very clear to any programmer that it does return a value, and much harder for them to break it without realizing it.

Alternatively, one could write it like this and leave out the return statement...

def plus_one_to_y(x)
@y = x + 1
puts "In plus_one_to_y"
@y
end

But why bother leaving out the word return? Why not just put it in there and make it 100% clear what's happening? It will literally have no impact on your code's ability to perform.

Good practice to explicitly return the values of the function?

It's The Ruby Way™ to not include the return statement unless it's absolutely required.

I'm sure there's better examples, but here's a simple one.

def divide a, b
return false if b == 0
a/b
end

It's worth noting that Ruby provides means to optionally ignore a lot of syntax. () are optional unless you're nesting them. {} can be omitted in many cases too.

func(5, 5, {:hello => 'world'})
# is the same as
func 5, 5, hello: 'world'

You'll learn a lot more shortcuts as you go.

Ruby explicit & implicit return confusions

It's doing both the long and short versions. You can see this by adding a puts "long" and puts "short" in your to_s function.

The problem is not about scope, but your reliance on the implicit return of the last evaluated expression in print_addresses. print_addresses doesn't print anything and it doesn't explicitly return anything. It dutifully calls address.to_s('long'), throws out the result, and returns the result of the last evaluated expression... address. address is then run through puts which results in it being converted to a string with the default short format. You can comment out the call to address.to_s and you'll get the same result.

This might seem weird that address is the last evaluated expression which is why you should always do an explicit return. It makes the code easier to read and saves everyone a lot of headaches. This also points out that your function is supposed to return more than one thing, something you can't easily do implicitly.

  def print_addresses(format = 'short')
puts "Addresses:"

formatted_addresses = []

case format
when 'short'
addresses.each do |address|
formatted_addresses << address.to_s('short')
end
when 'long'
addresses.each do |address|
formatted_addresses << address.to_s('long')
end
end

return formatted_addresses
end

As a side note, since print_addresses doesn't print it should be called format_addresses. It should also get rid of that redundant case statement. The bit of extra formatting (the "Address" header) goes into a wrapper method to leave format_addresses flexible.

  def display_addresses(format)
return ["Addresses: "] + format_addresses(format)
end

def format_addresses(format = 'short')
formatted_addresses = []

addresses.each do |address|
formatted_addresses << address.to_s(format)
end

return formatted_addresses
end

How to make a Ruby function act like an explicit 'return' statement?

throw/catch will do this.

Wrap the guts of your method in a call to Kernel#catch:

def something
catch :return do
foo! 123, 456
end
end

Have #foo! call Kernel#throw:

def foo!(*args)
throw :return, args
end

When #throw is called, it causes an exception which is caught by the #catch statement. The result of the #catch statement is then the value that was given to the #throw statement. Therefore:

p something     # [123, 456]

If #throw is not called, then the result of the #catch is the result of the last statement executed in its block.

Do all methods have to return a meaningful value?

You don't have to care about the return value if that method is not used as such with a certain expected value. Nothing to worry about.

But for your counts example, returning that value is the whole point of the method. If the method didn't return that value, then it is meaningless, and you definitely need that counts at the end.

There are some cases when the return value is not the main purpose of the method but you still want to return a certain value. One such case is when the method is intended to be used in a jQuery-style method chain like this:

some_object.do_this(args).do_that(args).then_do_this

In such case, it is important that you return the receiver. This happens in certain libraries or frameworks, but unless you specifically intent it to be used that way, you don't necessarily have to do it that way.

what is the point of return in Ruby?

return allows you to break out early:

def write_code(number_of_errors)
return "No problem" if number_of_errors == 0
badness = compute_badness(number_of_errors)
"WHAT?! Badness = #{badness}."
end

If number_of_errors == 0, then "No problem" will be returned immediately. At the end of a method, though, it's unnecessary, as you observed.


Edit: To demonstrate that return exits immediately, consider this function:

def last_name(name)
return nil unless name
name.split(/\s+/)[-1]
end

If you call this function as last_name("Antal S-Z"), it will return "S-Z". If you call it as last_name(nil), it returns nil. If return didn't abort immediately, it would try to execute nil.split(/\s+/)[-1], which would throw an error.

ruby explicit method return types

Update 2019: still no static typing or type annotations built into ruby, but we now have 3rd party type checkers. See this answer for more details.


Java performs static type checks. It ensures at compile time that all type requirements are met. Ruby is more dynamic, it doesn't do compile time checks and its type system is often referred to as "duck typing". That is, "If it walks like a duck and quacks like a duck, then it must be a duck".

If a method expects an object that responds to certain messages (has certain methods), then it doesn't care about actual type of the object as long it conforms to the protocol. If this is backed up by good test suite, then it's (arguably) a better way, because it allows for faster prototyping and reduced visual clutter. Less visual clutter - code is more expressive and understandable. Benefits of that are obvious. :)

Ruby Block statements and Implicit Returns

The implicit return value of a method is the last expression evaluated in the method.

In your case, neither of the two lines you annotated are the last expression. The last expression that gets evaluated is the assignment to words_array (which BTW is completely useless since because it is the last expression there is no way to use that variable afterwards).

Now, what is the value of an assignment expression? It is the value being assigned, in this particular case, the return value of the map method, which is an Array. So, that is what the method returns.

In the second example, at the very first iteration of the map, you will hit one of the two returns and thus immediately return from the method. In the first example, however, you will always iterate through the entire words array.

The problem is not that implicit and explicit returns are different, the problem is that the two lines you claim are implicit returns aren't.

implicit return with variable assignment

There is actually no rationale in assigning the local variable at all since it goes out of scope immediately. So the "correct way" to write the method is actually:

def some_function(x)
some_calculations(x)
end

Even if you do what to do some operations with the result you can use then and tap instead of assigning a local variable:

def some_function(x)
some_calculations(x).then do |value|
value ** 2
end
end


Related Topics



Leave a reply



Submit