Bug #11808
openDifferent behavior between Enumerable#grep and Array#grep
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 6 years ago
          Updated by jeremyevans0 (Jeremy Evans) over 6 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 6 years ago
          Updated by nobu (Nobuyoshi Nakada) over 6 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)
There is no API for it now.
        
           Updated by nobu (Nobuyoshi Nakada) over 6 years ago
          Updated by nobu (Nobuyoshi Nakada) over 6 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 6 years ago
          Updated by matz (Yukihiro Matsumoto) over 6 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 6 years ago
          Updated by ko1 (Koichi Sasada) over 6 years ago
          
          
        
        
      
      - Assignee set to ko1 (Koichi Sasada)
        
           Updated by ko1 (Koichi Sasada) about 5 years ago
          Updated by ko1 (Koichi Sasada) about 5 years ago
          
          
        
        
      
      Sorry we need more time to consider.
        
           Updated by noelrap (Noel Rappin) almost 2 years ago
          Updated by noelrap (Noel Rappin) almost 2 years 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.
        
           Updated by hsbt (Hiroshi SHIBATA) over 1 year ago
          Updated by hsbt (Hiroshi SHIBATA) over 1 year ago
          
          
        
        
      
      - Status changed from Open to Assigned
        
           Updated by jeremyevans0 (Jeremy Evans) 12 months ago
          Updated by jeremyevans0 (Jeremy Evans) 12 months ago
          
          
        
        
      
      - Related to Bug #20807: String#gsub fails when called from string subclass with a block passed added