Why is a Regexp object considered to be “falsy” in Ruby?

孤街醉人 提交于 2019-12-03 05:28:57

This isn’t a bug. What is happening is Ruby is rewriting the code so that

if /foo/
  whatever
end

effectively becomes

if /foo/ =~ $_
  whatever
end

If you are running this code in a normal script (and not using the -e option) then you should see a warning:

warning: regex literal in condition

This is probably somewhat confusing most of the time, which is why the warning is given, but can be useful for one lines using the -e option. For example you can print all lines matching a given regexp from a file with

$ ruby -ne 'print if /foo/' filename

(The default argument for print is $_ as well.)

This is the result of (as far as I can tell) an undocumented feature of the ruby language, which is best explained by this spec:

it "matches against $_ (last input) in a conditional if no explicit matchee provided" do
  -> {
    eval <<-EOR
    $_ = nil
    (true if /foo/).should_not == true
    $_ = "foo"
    (true if /foo/).should == true
    EOR
  }.should complain(/regex literal in condition/)
end

You can generally think of $_ as the "last string read by gets"

To make matters even more confusing, $_ (along with $-) is not a global variable; it has local scope.


When a ruby script starts, $_ == nil.

So, the code:

// ? 'Regexps are truthy' : 'Regexps are falsey'

Is being interpreted like:

(// =~ nil) ? 'Regexps are truthy' : 'Regexps are falsey'

...Which returns falsey.

On the other hand, for a non-literal regexp (e.g. r = // or Regexp.new('')), this special interpretation does not apply.

// is truthy; just like all other object in ruby besides nil and false.


Unless running a ruby script directly on the command line (i.e. with the -e flag), the ruby parser will display a warning against such usage:

warning: regex literal in condition

You could make use of this behaviour in a script, with something like:

puts "Do you want to play again?"
gets
# (user enters e.g. 'Yes' or 'No')
/y/i ? play_again : back_to_menu

...But it would be more normal to assign a local variable to the result of gets and perform the regex check against this value explicitly.

I'm not aware of any use case for performing this check with an empty regex, especially when defined as a literal value. The result you've highlighted would indeed catch most ruby developers off-guard.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!