Bug #4136
closedEnumerable#reject should not inherit the receiver's instance variables
Description
=begin
re
Below, you see that a.reject returns a copy of the receiver, which inherits the instance variable @foo. This is not the case with Array#select.
irb(main):001:0> a=[]
=> []
irb(main):002:0> a.instance_variable_set "@foo", "bar"
=> "bar"
irb(main):003:0> a.reject {}.instance_variable_get "@foo"
=> "bar"
irb(main):004:0> a.select {}.instance_variable_get "@foo"
=> nil
1.8.x behaves the same way.
=end
Updated by headius (Charles Nutter) about 14 years ago
=begin
I find this behavior unintuitive. #reject returns a new array, which I would not expect to have instance variables from the old array. It could, in fact, drag along data I don't intend for it to drag along, with no obvious way to scrub that data out other than manually removing instance vars on the new object.
Also note that this unnecessarily impacts the perf of reject when ivars are present, and if people want this behavior, .dup.reject! is an easy way to get it.
=end
Updated by hasari (Hiro Asari) about 14 years ago
=begin
In the similar vein, if you subclass Array, that class's #reject returns an object of that subclass, rather than an Array.
$ irb
irb(main):001:0> RUBY_DESCRIPTION
=> "ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]"
irb(main):002:0> class A < Array; def foo; return "yo"; end; end
=> nil
irb(main):003:0> a=A.new
=> []
irb(main):004:0> a.reject {}.foo
=> "yo"
irb(main):005:0> a.collect { true }.foo
NoMethodError: undefined method `foo' for []:Array
from (irb):5
from :0
This is counterintuitive, at the least.
=end
Updated by matz (Yukihiro Matsumoto) about 14 years ago
- Status changed from Open to Closed
- % Done changed from 0 to 100
=begin
This issue was solved with changeset r30148.
Hiro, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.
=end
Updated by zenspider (Ryan Davis) about 14 years ago
=begin
On Dec 8, 2010, at 10:37 , Hiro Asari wrote:
irb(main):001:0> a=[]
=> []
irb(main):002:0> a.instance_variable_set "@foo", "bar"
=> "bar"
irb(main):003:0> a.reject {}.instance_variable_get "@foo"
=> "bar"
irb(main):004:0> a.select {}.instance_variable_get "@foo"
=> nil
that's an awesome find.
=end
Updated by mudge (Paul Mucur) about 14 years ago
=begin
I have attempted to codify this new behaviour in RubySpec in https://github.com/rubyspec/rubyspec/pull/31 and would appreciate any feedback.
=end
Updated by marcandre (Marc-Andre Lafortune) about 14 years ago
- Category set to core
=begin
This changes the behavior for subclasses of Array. Should the other cases also be modified in the same way?
If I check the list I had in my blog (see the quiz at bottom of http://blog.marc-andre.ca/2009/05/schizo-ruby-puzzle.html ), the following (at least) are remaining:
Sub = Class.new(Array)
x = Sub.new
(x * 2).class # => Sub
x.flatten.class # => Sub
x[0...0].class # => Sub
=end
Updated by matz (Yukihiro Matsumoto) about 14 years ago
=begin
Hi,
In message "Re: [ruby-core:33704] [Ruby 1.9-Bug#4136] Enumerable#reject should not inherit the receiver's instance variables"
on Tue, 14 Dec 2010 07:42:26 +0900, Marc-Andre Lafortune redmine@ruby-lang.org writes:
|This changes the behavior for subclasses of Array. Should the other cases also be modified in the same way?
If a method is originally defined in Enumerable, i.e. its return value (Array)
is a collection of values from enumerable.
|If I check the list I had in my blog (see the quiz at bottom of http://blog.marc-andre.ca/2009/05/schizo-ruby-puzzle.html ), the following (at least) are remaining:
|
|Sub = Class.new(Array)
|x = Sub.new
|(x * 2).class # => Sub
|x.flatten.class # => Sub
|x[0...0].class # => Sub
I don't think so. #flatten is not an enumerable method. Please point
out if we missed some other methods.
matz.
=end
Updated by Eregon (Benoit Daloze) about 14 years ago
=begin
On 15 December 2010 05:32, Marc-Andre Lafortune
ruby-core-mailing-list@marc-andre.ca wrote:
Or similarly, why does:
(x.slice(0,0)).class # => Sub
# while...
(x.slice!(0,0)).class # => ArrayThanks¶
Marc-André
I suppose you already know this, but it might help others:
x.slice!(0, 1).class # => Sub
x.slice!(0..0).class # => Sub
x.slice!(0, 0).class # => Array
x.slice!(1, 0).class # => Array
x.slice!(1..0).class # => Array
So, it seems any arguments to #slice which return some kind of Array
except slice(n, 0) and slice(n, <n) return a Sub.
This is inconsistent IMHO, because it should return the same class,
even when the array is empty (for the cases it returns some kind of
Array).
Also, a mutating method should (most of the time) not change an object's class.
Regards,
B.D.
=end