Double ampersand in Ruby
I am using the authlogic gem with Ruby on Rails, and I have been using the following to obtain the id of the user that is currently logged in:
current_user = UserSession.find
id = current_user && current_user.record.id
I'm not understanding how current_user && current_user.record.id returns the current user id. I would think this would return a boolea开发者_如何学JAVAn. Can someone explain how this works?
There is no Boolean
type in Ruby; Ruby has a rather simple view of truth (or more precisely, it has a rather simple view of falsehood).
- the
false
object, which is the singleton instance ofFalseClass
is considered falsy - the
nil
object, which is the singleton instance ofNilClass
is falsy - every other object is truthy (including, obviously, the
true
object, which is the singleton instance ofTrueClass
)
[BTW: this means that a lot of objects that are considered falsy in some other languages, are actually truthy in Ruby, like the integer 0
, the real value 0.0
, the empty string, the empty array, the empty hash, the character 'F'
]
So, the Boolean operators &&
, ||
, and
and or
do not return Boolean values. Instead they return the first object that determines the outcome of the expression.
(They are also short-circuiting, which means that they only evaluate the minimum sub-expressions that are needed to determine the outcome of the expression. So, an alternate formulation would be that they return the result of the last expression that was evaluated. Which, in turn, is analogous to what blocks, methods, class bodies and module bodies do.)
So, what does it mean to return the first object that determines the outcome? That's simple, really: the result of the expression
a && b
is truthy if both a
and b
are truthy, otherwise it is falsy. So, if a
is falsy, it is completely irrelevant what b
is: the result will be falsy either way. So, we might just as well simply return a
. (Remember, a doesn't have to be false
, it could also be nil
and the programmer might want to know which one of the two it was.)
If, OTOH, a
is truthy (IOW it is neither nil
nor false
), then the result of the whole expression is solely dependent on b
: if b
is truthy, the whole result will be truthy, otherwise if b
is falsy, the whole result will be falsy. So, we might just as well return b
itself, instead of first converting it to a Boolean.
||
and or
are analogous or more precisely dual to &&
and and
.
You posted this example:
id = current_user && current_user.record.id
Here, the author isn't even expecting current_user
to be a Boolean value! Instead, he expects it to be either a User
or nil
. But that's perfectly fine, because a User
is truthy and nil
is falsy, so they still can be used in a Boolean expression.
The basic intention is that the author wants to prevent a NoMethodError
exception being raised, if he tries to call #record
on nil
.
An alternative way of expressing this would be
id = current_user.record.id unless current_user.nil?
If you want all the gory details, check out Section 11.1 (page 36) of the Draft ISO Ruby Specification or the excutable specifications of the RubySpec project. (Here's the one for &&
.)
I wrote a pure Ruby implementation of Ruby's Boolean operators and conditional expressions once for fun. The meat of the implementations is these two mixins.
The logical and is short circuiting. That means that if the construct is X && Y, and X is false then Y never gets checked because the whole thing is certainly going to be yield false.
That code is saying, essentially:
if (current_user is TRUE) {
id = current_user.record.id;
]
Here's some console output showing you get the second value if the first is true:
irb(main):005:0> true && 9
=> 9
and nil if the first is nil:
irb(main):008:0> nil && 9
=> nil
精彩评论