Project

General

Profile

Actions

Bug #11808

open

Different behavior between Enumerable#grep and Array#grep

Added by BenOlive (Ben Olive) over 8 years ago. Updated 4 months ago.

Status:
Open
Target version:
-
[ruby-core:72067]
Tags:

Description

Regex special global variables are available within the block for Array#grep, but are nil within the block for Enumerable#grep.

Here is an example that explains it better:

class Test
  include Enumerable
  def each
    return enum_for(:each) unless block_given?
    yield "Hello"
    yield "World"
  end
end

enum = Test.new
array = ["Hello", "World"]

enum.grep(/^(.)/) {$1} # => [nil, nil]
array.grep(/^(.)/) {$1} # => ["H", "W"]

Tested on 2.0.0, 2.1.5, & 2.2.2

Updated by jeremyevans0 (Jeremy Evans) over 4 years ago

Array#grep is actually Enumerable#grep:

Array.instance_method(:grep).owner
=> Enumerable

If I had to guess, the cause of the difference is that Array#each is implemented in C, and Test#each is implemented in Ruby, and this affects Regexp special variable scope. You see similar behavior as Array in other classes that implement #each in C, such as Range or File.

The documentation for the special global variables states: These global variables are thread-local and method-local variables. This indicates to me that the bug is that the variables are accessible inside the Array#each block, since that block executes inside the current method, it's not local to the Array#each method. However, I would assume removing the current behavior would break too much existing code.

Updated by nobu (Nobuyoshi Nakada) over 4 years ago

  • Tracker changed from Bug to Feature
  • Description updated (diff)
  • ruby -v deleted (2.2.2)
  • Backport deleted (2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN)
Actions #3

Updated by nobu (Nobuyoshi Nakada) over 4 years ago

  • Subject changed from DIfferent behavior between Enumerable#grep and Array#grep to Different behavior between Enumerable#grep and Array#grep

Updated by matz (Yukihiro Matsumoto) over 4 years ago

  • Tracker changed from Feature to Bug
  • Backport set to 2.5: UNKNOWN, 2.6: UNKNOWN

It is a bug. It has been hidden for 10+ years and seems to be very difficult to fix.
It should be fixed in the long run.

Matz.

Updated by ko1 (Koichi Sasada) over 4 years ago

  • Assignee set to ko1 (Koichi Sasada)

Updated by ko1 (Koichi Sasada) over 3 years ago

Sorry we need more time to consider.

Updated by noelrap (Noel Rappin) 4 months ago

This appears to be fixed in 3.3.0dev as of Nov 2023,

irb(main):001* class Test
irb(main):002*   include Enumerable
irb(main):003*   def each
irb(main):004*     return enum_for(:each) unless block_given?
irb(main):005*     yield "Hello"
irb(main):006*     yield "World"
irb(main):007*   end
irb(main):008> end
=> :each
irb(main):009>
irb(main):010> enum = Test.new
=> #<Test:0x0000000102ba5038>
irb(main):011> array = ["Hello", "World"]
=> ["Hello", "World"]
irb(main):012>
irb(main):013> enum.grep(/^(.)/) {$1}
=> ["H", "W"]

However, it was still broken in 3.2.2. It's not clear to me when the behavior changed.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0