0

This doesn't really make logical sense to me. Let's say you're doing something like this:

@current_user ||= User.find_by(id: :session[:user_id])

It seems to me to make logical sense to check whether @current_user is nil. Then if it is nil do the OR operation.

So why is this written in ruby as ||= rather than =||?

Same goes for something like this:

x += 1
Mureinik
  • 297,002
  • 52
  • 306
  • 350
Lee McAlilly
  • 9,084
  • 12
  • 60
  • 94
  • 1
    Possible duplicate of [What does ||= (or-equals) mean in Ruby?](http://stackoverflow.com/questions/995593/what-does-or-equals-mean-in-ruby) – Eyeslandic Mar 11 '17 at 20:01
  • 2
    Because most of the preceding languages did it that way (for their own reasons) most likely. – mu is too short Mar 11 '17 at 20:12
  • I think one of the Ruby books said it's based on Perl's use of `||=`. – the Tin Man Mar 11 '17 at 22:34
  • 1
    As for `x += 1`, that really does evaluate to `x = x + 1` and not `x + x = 1` (which makes no sense anyway). But consider the alternative: if it were `x=+1` that's really ambiguous, did you mean to use the compound assignment operator, or just `x = (+1)`? – philomory Mar 11 '17 at 23:48

1 Answers1

6

It's a common misconception in Ruby to think that

@current_user ||= User.find_by(id: :session[:user_id])

expands out to

@current_user = @current_user || User.find_by(id: session[:user_id])

However, Ruby actually expands the expression in this case as

@current_user || @current_user = User.find_by(id: session[:user_id])

taking advantage of the || short circuit to only assign the @current_user variable when it is not set.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Leo Correa
  • 19,131
  • 2
  • 53
  • 71
  • Just curious, is there a way to test that's it's really `a || a = b` and not `a = a || b` ? – Eric Duminil Mar 11 '17 at 23:33
  • 1
    @EricDuminil The implementation of `||=` is actually fairly complicated with a bunch of edge cases, but conceptually you can check like this: 1) create a class `Foo` with `attr_accessor :bar` 2) define a method `foo` that prints `"called :foo"` then returns `@bar`; 3) define a method `foo=(val)` that prints `"called :foo="`; 4) `x = Foo.new`; 5) `x.foo ||= 1` prints "called :foo" then "called :foo="; 6) `x.bar = 1`; 7) now `x.foo ||= 1` prints "called :foo" but does not print "called :foo=". So you can see `foo=` is not called if `foo` evaluates to something other than `false` or `nil`. – philomory Mar 11 '17 at 23:43
  • @philomory: Thanks a lot! – Eric Duminil Mar 11 '17 at 23:48