Implicit return values in Ruby
Any statement in ruby returns the value of the last evaluated expression.
You need to know the implementation and the behavior of the most used method in order to exactly know how your program will act.
#each
returns the collection you iterated on. That said, the following code will return the value of line.scan(regexp).
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
If you want to return the result of the execution, you can use map
, which works as each
but returns the modified collection.
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).map do |matched|
matched.join("|")
end.join("\n") # remember the final join
end
end
end
There are several useful methods you can use depending on your very specific case. In this one you might want to use inject
unless the number of results returned by scan
is high (working on arrays then merging them is more efficient than working on a single string).
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).inject("") do |output, matched|
output << matched.join("|") << "\n"
end
end
end
end
Implicit return value scope in Ruby
You will want to change to:
def self.include?(array, search_item)
array.each do |elem|
if elem == search_item
return true
end
end
return false
end
The reason for this is because the last statement in the method is the return value. In your case you never return false if there is no match. Be sure to add the return
when you want to break out and stop the rest of the method execution immediately.
Explicit vs Implicit return
A methods ends when one of the following occurs
- Immediately when the thread terminates
- Immediately when a
raise
/throw
is encountered - excepting as such is handled - Immediately when a
return
is encountered - When it runs out of code or "reaches the end", in which case the value of the last expression is returned
The immediate forms end the method immediately (no surprise), while simple pure expressions such as true
or false
as appearing by themselves on a line by themselves (read: an expression) mean nothing unless such values are themselves used - but this is not done the return is removed.
Whats up with implicit return values in Ruby?
Make sure not to add puts
statements to the end of a method or a block unless you want to return nil.
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
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
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 return
s 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.
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.
Implicit return value of Array#map in Ruby
Because Ruby assignments always return the item they were passed, regardless of what the setter=
method returns.
See also Is it possible to have class.property = x return something other than x?
Related Topics
How to Encode Media in Base64 Given Url in Ruby
How to Check If a Value Is a Number
Guard with Rspec on Rails 4 Giving a Lot of Warnings
How to Fix Rubygems Recent Deprecation Warning
Rails - Whenever Gem - Dynamic Values
Where to Define Custom Error Types in Ruby And/Or Rails
Ruby on Rails: How to Run Things in the Background
What Does 'If _File_ == $0' Mean in Ruby
What Are the Differences Between "Private", "Public", and "Protected Methods"
Csv.Read Illegal Quoting in Line X
What to Use Instead of 'Render :Text' (And 'Render Nothing: True') in Rails 5.1 and Later
Strange Inability to Require Config/Boot After Upgrading to Ruby 1.9.2
How to Remove Permission Denied @ Rb_Sysopen - Gem Install Error
Project Euler 1:Find the Sum of All the Multiples of 3 or 5 Below 1000
Docker for MAC - Mkmf.Rb Can't Find Header Files for Ruby
Installing Cocoapods: No Response
Error Installing Nokogiri: Failed to Build Gem Native Extension & Libiconv Is Missing (Osx)