Bug #17739
closedArray#sort! changes the order even if the receiver raises FrozenError in given block
Description
I think this is a similar issue of https://bugs.ruby-lang.org/issues/17736
array = [1, 2, 3, 4, 5]
begin
array.sort! do |a, b|
array.freeze if a == 3
1
end
rescue => err
p err #=> #<FrozenError: can't modify frozen Array: [5, 4, 3, 2, 1]>
end
p array #=> [5, 4, 3, 2, 1]
array = [1, 2, 3, 4, 5]
array.sort! do |a, b|
break if a == 3
1
end
p array #=> [3, 4, 2, 1, 5]
Array#sort! raises a FrozenError as expected, but the order is changed. I would expect the order is kept after frozen.
Updated by chrisseaton (Chris Seaton) almost 4 years ago
I think the point of #sort!
is that it sorts in-place - so not creating a copy. If we want to be able to restore the unsorted array after an exception happens half-way through sorting, we'd need to create a copy to either swap back in after an exception, or to sort and then swap in on success. If we did either of those... we might as well not have #sort!
at all and advise people to just use #sort
, as you'd never really be 'sorting in place'.
Updated by marcandre (Marc-Andre Lafortune) almost 4 years ago
Is there a usecase for this?
Updated by jeremyevans0 (Jeremy Evans) over 3 years ago
I think this is a bug, but it's not that Array#sort!
should keep the initial order of the receiver, it's that it should keep the order at the point freeze
is called, and it does not:
array = [1, 2, 3, 4, 5]
begin
array.sort! do |a, b|
array.freeze if a == 3
p [array.frozen?, array]
1
end
rescue => err
p err #=> #<FrozenError: can't modify frozen Array: [5, 4, 3, 2, 1]>
end
Output
[false, [1, 2, 3, 4, 5]]
[true, [1, 2, 3, 4, 5]]
[true, [5, 2, 3, 4, 1]]
[true, [5, 2, 3, 4, 1]]
[true, [5, 3, 2, 4, 1]]
[true, [5, 3, 2, 4, 1]]
#<FrozenError: can't modify frozen Array: [5, 3, 1, 4, 2]>
Notice that the array's order is changed after it has been frozen.
I've added a pull request to fix this: https://github.com/ruby/ruby/pull/4335
Updated by matz (Yukihiro Matsumoto) over 3 years ago
Accepted. I have small concerns about performance (in microbenchmarks). But I don't think I have another choice.
Matz.
Updated by jeremyevans (Jeremy Evans) over 3 years ago
- Status changed from Open to Closed
Applied in changeset git|8b00bfb7c2c33827490c78a16c44b102cb0d724b.
Do not allow array modifications after freeze inside sort!
If freezing an array inside sort!, previously the array could be
modified after the freeze. This checks whether the receiver is
frozen after every yield and potential call to #> or #<,
preventing modifications if the receiver is frozen inside the
block or by the #> or #< call.
Fixes [Bug #17739]