10

For other types of variables, I use ||=, but this doesn't work for booleans (x ||= true assigns x to true even if x was previously assigned to false).

I'd thought that this would work:

x = true unless defined?(x)

But it doesn't: it assigns x to nil for some reason. (An explanation here would be appreciated.)

I do know one method that works:

unless defined?(x)
  x = true
end

But it's rather verbose. Is there a more concise way to assign default values to boolean variables in Ruby?

evanrmurphy
  • 1,826
  • 1
  • 18
  • 19
  • is there a reason why you do not want x to be assigned to nil (which is equivalent to false for all intent and purposes) ? – sylvain.joyeux Nov 09 '12 at 06:51
  • @sylvain.joyeux Because he wants it to be `true`, not falsey. – Andrew Marshall Nov 10 '12 at 00:24
  • @sylvain.joyeux Using nil instead of false is fine, but it doesn't address the trouble with `||=`. If you try to assign a default value to x using `||=`, and x was previously assigned to nil, it will be overridden rather than retaining the value of nil (since nil is falsey). – evanrmurphy Nov 10 '12 at 13:37

3 Answers3

12

You must have defined? first, else the parser reaches x = and then defines x (which makes it nil) before running the unless:

defined?(x) or x = true
x  #=> true
x = false
defined?(x) or x = true
x  #=> false

Doing a if/unless block (instead of post-if/unless one-liner) also works:

unless defined?(x)
  x = true
end
x  #=> true
x = false
unless defined?(x)
  x = true
end
x  #=> false
Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
3

There are only two non-true values in Ruby: false and nil. All you need to do is differentiate between those. Until the new //= operator that does this automatically comes around, you're stuck with this:

if (x.nil?)
  x = true
end

Hopefully this can be abbreviated in future versions of ruby. 99% of the time you don't really care about the difference between the two non-true values, but that 1% of the time you do it becomes annoying to have to be so unusually verbose.

Remember that the defined? operator will always return "local-variable" for that condition because the variable x is "defined" as a local variable. Contrast with defined?(nope) and you'll get nil because that variable does not exist. Ruby is concerned with the variable or constant in question, not if that variable or constant has been defined with a value.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • 4
    Or `x = true if x.nil?`, which to me is more readable than the `||=` idiom anyway! – Gene Nov 09 '12 at 05:12
  • I know this might seem obvious but you need to have `x` somewhere in your code because `class A; def self.check; if x.nil?; x = true; end; end; end; A.check;` will return an `undefined local variable` exception. – sunnyrjuneja Nov 09 '12 at 05:13
  • Depending on the cotext of the poster, this might actually be wrong. If you do this in IRB, it causes an `undefined local variable` exception where as the working example the poster does not and assigns `x = true`. – sunnyrjuneja Nov 09 '12 at 05:14
  • 1
    Thanks for the reply, but your example is only good if x hasn't been previously defined. Otherwise, it fails with `NameError: undefined local variable or method 'x' for main:Object`. In my case, it's uncertain whether x will be previously defined, which is why I was checking with `defined?`. Sorry if this was unclear in the question. – evanrmurphy Nov 09 '12 at 05:15
  • Yeah, you have to define variables somewhere before using them. It's better to use instance variables like `@x` which can't not exist. – tadman Nov 09 '12 at 06:22
-1
x = defined?(x) ? true  : false
Sirko
  • 72,589
  • 19
  • 149
  • 183
vijikumar
  • 1,805
  • 12
  • 16