Is there any difference between using the || operator and rescue in Ruby?
Say:
b = A.value || "5"
b = A.value rescue 5
where the object A does not have value method.
|| is the boolean or operator (keep in mind that in Ruby, only the values nil and false evaluates to false, in boolean context):
nil || 5
# => 5
false || 5
# => 5
4 || 5
# => 4
rescue is for exception catching:
fail 'bang' rescue 5
# => 5
'bang' rescue 5
# => "bang"
nil rescue 5
# => nil
In your examples, given that A do not respond to value:
A.value
# NoMethodError: undefined method `value' ...
b = A.value || 5
# NoMethodError: ...
b
# => nil
b = A.value rescue 5
b
# => 5
Now suppose that A.value returns nil:
A.value
# => nil
b = A.value || 5
b
# => 5
b = A.value rescue 5
b
# => nil
The || is an or operator. Your first line reads:
Set b to A.value; if not b (i.e. b is nil or false), then set it to the string "5".
Rescue lets you recover from exceptions. Your second line reads:
Set b to A.value. If A.value raises an exception, ignore the problem and set b to 5 instead.
For an object A with no value method, the first line will crash the app.
For an object A whose value method returns nil, the second line will set b to nil.
Apart from what others already told you, one more difference is, that || is an honest operator, while inline rescue is tantamount to rescuing StandardError, which is someting that various manuals of style passionately frown upon. In other words, inline rescue is an indecent hack, that should not be used too frequently in production code. Use decently dressed begin ... rescue ... else ... ensure ... end statement instead.
Related
Just tripped over an instruction that is unclear:
a = 5 || 3 # ==> 5
verdict = true || false # ==> true
Since || is the same as or, how and/or why would this statement be used? It doesn't really demonstrate any decision making other than choosing the first option always.
I know the ||= assignment, but this is different. Looking for clarification on the usage above of || alone.
a = b || c
What this statement does is, it tells Ruby to 'assign the value of b to a, unless b is falsy, if b is falsy, assign the value of c to a. In case b isn't falsy, the c statement won't get executed.
A good example where you could use this is if you're getting a variable from somewhere and you're not sure if it's going to be nil or not, so you create a variable like c as a second option.
If you have a method that takes in a hash as a parameter for example, and you want to return the value of the element from the hash that has the key 'b' for example, but the hash parameter won't always have a 'b' key, so you write something like this
def value_of_b(hash)
b_val = hash['b'] || 'unknown'
puts "The value of b is :#{b_val}"
end
h = {'a' => 1, 'b' => 2}
value_of_b(h)
#=>The value of b is :2
m = {'a' => 1}
value_of_b(m)
#=>The value of b is :unknown
Another example that comes to my mind is accessing an array element that doesn't exist
[1,2,3][3] || "default"
#=> "default"
Or having a default value for Rails params hash:
#name = params[:name] || "no name provided"
I understand there is a difference in the precedence as shown in another answer:
p foo = false || true
# => true
p foo = false or true
# => false
But it seems like there is something more that's different between or and ||.
For example:
p foo = 42 or raise "Something went wrong with foo"
# => 42
p foo = nil or raise "Something went wrong with foo"
# => Something went wrong with foo (RuntimeError)
p foo = 42 || raise "Something went wrong with foo"
# => syntax error, unexpected tOP_ASGN, expecting end-of-input
I was expecting to get:
p foo = 42 or raise "Something went wrong with foo"
# => 42
p foo = nil or raise "Something went wrong with foo"
# => Something went wrong with foo (RuntimeError)
p foo = 42 || raise "Something went wrong with foo"
# => Something went wrong with foo (RuntimeError)
But it's a syntax error. So what is happening?
Theory :
Here's a precedence table for Ruby.
It's not clear from this table but Ruby method invocation without parentheses has a lower precedence than || and =, but higher than or. See this question.
So for your code, from highest to lowest precedence :
||
=
raise "something"
or
Expression with or
foo = 42 or raise "Something went wrong with foo"
First comes = :
( foo = 42 ) or raise "Something went wrong with foo"
Then raise :
( foo = 42 ) or ( raise "Something went wrong with foo" )
Then or :
( ( foo = 42 ) or ( raise "Something went wrong with foo" ) )
Expression with ||
foo = 42 || raise "Something went wrong with foo"
First comes || :
foo = ( 42 || raise ) "Something went wrong with foo"
Here's your syntax error!
You want :
foo = 42 || (raise "Something went wrong with foo") #=> 42
or
foo = 42 || raise("Something went wrong with foo") #=> 42
or just
foo = 42 || raise
Warning!
When you have troubles with precedence, you should be careful about adding another puts or p without parentheses !
For example :
p [1,2,3].map do |i|
i*2
end
outputs :
#<Enumerator: [1, 2, 3]:map>
even though you might have expected :
[2, 4, 6]
|| and or are not the same operation.
The first is equivalent to a method call, the latter is a control flow keyword. You probably always want to use || to avoid confusion with precedence. Most style guides for Ruby have a clause that bans the use of and and or for that reason.
So then,
A or B
# can be considered equivalent to
if A then A else B end
whereas
A || B
# can be considered equivalent to
A.or { B } # given a hypothetical "logical or" method
Now let's look into your or example
p foo = false or true
is equivalent to
temp = p(foo = false) # => nil
if temp
temp
else
true
end
and thus when executed prints false and returns true
[1] pry(main)> p foo = false or true
false
=> true
[2] pry(main)> foo
=> false
whereas
p foo = false || true
is equivalent to (glossing over the difference between boolean and logical OR for now since your example is dealing with booleans anyway)
p(foo = false.|(true))
and thus when executed prints true and returns true
[1] pry(main)> p foo = false || true
true
=> true
[2] pry(main)> foo
=> true
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm newbie in Ruby. What is printed and why?
a = nil
b = 2
a ||= b
puts a
explain me about this simple code.
2 is printed, because ||= assigns the right-hand value to the left-hand variable when the left-hand variable is nil or false. a is nil, so 2 will be assigned to a. Therefore 2 is printed.
A lot of programming requires understanding Boolean logic, basically how true and false values are determined, which are what trigger conditional logic to branch, loop, etc.
Consider this:
In Ruby there are true and false values, which have equivalent reserved words of true and false (it actually goes deeper than this but that's for a different time.):
true # => true
false # => false
Only nil and false are false values in Ruby, everything else is true. This differs from some other languages, like Perl, which thinks that 0 and '' (empty string) are also false. Remember though, only nil and false in Ruby are false.
The "not" values (AKA, opposites) of true and false are false and true respectively. The ! (Boolean "NOT") operator is used for this:
!true # => false
!false # => true
Now, this is a trick you'll see advanced programmers use: NOT-ing them twice returns a boolean version of their original values:
!!true # => true
!!false # => false
This is an important thing to understand. We can use !! on values besides true/false and determine whether they are true or false values:
!!'' # => true # !> string literal in condition
!!0 # => true # !> literal in condition
!!'foo' # => true # !> string literal in condition
!!1 # => true # !> literal in condition
Ruby isn't too happy about me using a literal for the test, but you can see that the above are all true, and these are false:
!!false # => false
!!nil # => false
Hopefully you see how we have to look at values in a language two ways, whether they're true/false and what their actual value is.
Moving on:
The || (Boolean "OR") looks at the left value first. If it's true it returns that value. If it's false, it looks at the right side and returns that:
true || false # => true
false || true # => true
nil || 'foo' # => "foo"
false || 2 # => 2
The ||= operator behaves similarly, only after the comparison, it assigns to the variable on the left if it was a false-type value:
a = false
a ||= true
a # => true
a = true
a ||= false
a # => true
Assigning to variables, instead of testing directly against the literals, results in similar results:
a = nil # => nil
!a # => true
!!a # => false
b = 2 # => 2
!b # => false
!!b # => true
A bit more:
||= is used as shorthand to replace a little bit of if/then code:
a = nil # => nil
b = 2 # => 2
if !a
a = b
end
a # => 2
a = 1 # => 1
b = 2 # => 2
if !a
a = b
end
a # => 1
And all that leads up to the code you're trying to understand:
a = nil # => nil
b = 2 # => 2
a ||= b # => 2
I believe that ||= is a shorthand way of saying "if a is false assign it to..." in this case b
So
a = nil
b = 2
a ||= b
puts a
> 2
and
a = 4
b = 2
a ||= b
puts a
> 4
I have #obj.items_per_page, which is 20 at the beginning, and I want the method below to assign value to it only if many_items is not nil:
def fetch_it_baby (many_items = nil)
#obj.items_per_page = many_items
With the code above, even if many_items is nil, #obj.items_per_page remains at 20. Why? And is that "good" coding? Shouldn't I use something like
#obj.items_per_page = many_items || #obj.items_per_page
Or is there a third way? I don't feel completely comfortable with either way.
The style I generally see looks like this:
#obj.items_per_page = many_items if many_items
This uses the inline conditional, while avoiding negative or double-negative conditions.
I suggest the following as it makes it clear that you have a default value for the assignment in case the caller did not specify many_items in the call:
def function(argument = nil)
variable = argument || 20
...
end
However, since you specified that the assignment takes places only if the value is not nil then you'll need to check for the nil value otherwise you will miss the assignment if the value was false. If you really need that case then the solution is more long-winded:
def function(argument = nil)
variable = argument.nil? ? 20 : argument
...
end
You can use &&= (in the same way as ||= is used to assign only if nil or false)
> a = 20 # => 20
> a &&= 30 # => 30
> a # => 30
> a = nil # => nil
> a &&= 30 # => nil
> a = false # => false
> a &&= 30 # => false
> a = {} # => {}
> a &&= 30 # => 30
remember though
> a = 30 # => 30
> a &&= nil # => nil
> a &&= false # => nil
> b &&= 3 # => nil
Even if many_items is nil #obj.items_per_page remains at 20
That sounds like whatever class #obj is has a custom modifier method items_per_page= that only updates the value if the new value is not nil. This is not standard Ruby. For example, given this definition:
class Demo
attr_accessor :items_per_page
end
I get this behavior:
irb(main):005:0> demo = Demo.new #=> #<Demo:0x007fb7b2060240>
irb(main):006:0> demo.items_per_page = 20 #=> 20
irb(main):007:0> demo.items_per_page #=> 20
irb(main):008:0> demo.items_per_page = nil #=> nil
irb(main):009:0> demo.items_per_page #=> nil
As for your example, I would probably write it this way:
#obj.items_per_page = many_items unless many_items.nil?
For Rails you can also use presence as described here
region = params[:state].presence || params[:country].presence || 'US'
new-alexandria's answer is my go-to, but another "third way" would be to use a ternary:
class Demo
attr_accessor :items_per_page
end
many_items = 100
#obj = Demo.new
#obj.items_per_page = 20 #=> 20
#obj.items_per_page = !many_items.nil? ? 30 : nil #=> 30
I am using Rails and I have a similar need.
You can define a method on your model:
class Gift < ApplicationRecord
def safe_set(attribute, value)
return if value.nil?
send("#{attribute}=", value)
end
end
So you can do
g = Gift.new
g.colour = 'red'
g.safe_set(:colour, nil)
g.colour -> 'red'
g.safe_set(:colour, 'green')
g.colour -> 'green'
We have one more method in rails that can help to remove values that are nil.
compact
This method can be used with an array, hash. Returns the same data removing all values that are nil
Eg :
array.compact
Further reference:
https://apidock.com/ruby/v1_9_3_392/Array/compact
If many items is a variable the if/unless versions above are the best. But if many_items is actually a method you don't want to evaluate multiple times I find the following useful.
#obj.items_per_page = many_items.presence || #obj.items_per_page
This isn't quite what you want since it won't assign empty strings or other non-nil but non-present values but most of the time I want to do this kind of thing this works for me (it does create a self-assignment when the value is nil/non-present so only use it if your setters are idempotent).
Two similar sentences have different behaviour. Is it ok?
Compare:
a = 123 unless defined? a
a # => nil
but...
unless defined? b
b = 123
end
b # => 123
Yes, this is the correct behaviour. Local variables are created and initialized with nil before assignment. So this code
a = 123 unless defined? a
a # => nil
is a rough equivalent of
a = nil
a = 123 unless defined? a # `a` is not undefined anymore.
a # => nil
Another example (even though c is not defined before this line, this code does not throw a NameError).
c = 2 unless c # => 2