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 5 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 5 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 5 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 5 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 5 years ago
- Assignee set to ko1 (Koichi Sasada)
Updated by ko1 (Koichi Sasada) about 4 years ago
Sorry we need more time to consider.
Updated by noelrap (Noel Rappin) about 1 year 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) 8 months ago
- Status changed from Open to Assigned
Updated by jeremyevans0 (Jeremy Evans) 3 days ago
- Related to Bug #20807: String#gsub fails when called from string subclass with a block passed added