Skip to content

Add new Style/ReverseFind cop#14750

Merged
bbatsov merged 1 commit intorubocop:masterfrom
koic:add_new_style_reverse_find_cop
Jan 15, 2026
Merged

Add new Style/ReverseFind cop#14750
bbatsov merged 1 commit intorubocop:masterfrom
koic:add_new_style_reverse_find_cop

Conversation

@koic
Copy link
Member

@koic koic commented Dec 25, 2025

Ruby 4.0 introduces Array#rfind:
https://bugs.ruby-lang.org/issues/21678

This PR adds new Style/ReverseFind cop that identifies places where array.reverse.find can be replaced by array.rfind.

Safety

This cop is unsafe because it cannot be guaranteed that the receiver is an Array or responds to the replacement method.

Example

# bad
array.reverse.find { |item| item == 42 }
array.reverse.detect { |item| item == 42 }
array.reverse_each.find { |item| item == 42 }
array.reverse_each.detect { |item| item == 42 }

# good
array.rfind { |item| item == 42 }

Additional Information

Using this approach improves performance as follows:

require 'benchmark/ips'

Benchmark.ips do |x|
  array = *(1..100)

  x.report('reverse.find') { array.reverse.find { it == 42 } }
  x.report('reverse_each.find') { array.reverse_each.find { it == 42 } }
  x.report('rfind') { array.rfind { it == 42 } }

  x.compare!
end
$ ruby rfind.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
        reverse.find    81.778k i/100ms
   reverse_each.find    59.397k i/100ms
               rfind    89.719k i/100ms
Calculating -------------------------------------
        reverse.find    813.856k (± 0.7%) i/s -      4.089M in   5.024325s
   reverse_each.find    596.506k (± 0.4%) i/s -      3.029M in   5.078403s
               rfind    889.829k (± 0.5%) i/s -      4.486M in   5.041482s

Comparison:
               rfind:   889828.8 i/s
        reverse.find:   813855.8 i/s - 1.09x  slower
   reverse_each.find:   596505.7 i/s - 1.49x  slower

This cop is placed under Style rather than Performance because it encourages using the dedicated rfind method, which is considered a matter of style. As a result, the code becomes shorter and requires fewer method calls, resulting in a simpler style.


Before submitting the PR make sure the following are checked:

  • The PR relates to only one subject with a clear title and description in grammatically correct, complete sentences.
  • Wrote good commit messages.
  • Commit message starts with [Fix #issue-number] (if the related issue exists).
  • Feature branch is up-to-date with master (if not - rebase it).
  • Squashed related commits together.
  • Added tests.
  • Ran bundle exec rake default. It executes all tests and runs RuboCop on its own code.
  • Added an entry (file) to the changelog folder named {change_type}_{change_description}.md if the new code introduces user-observable changes. See changelog entry format for details.

Ruby 4.0 introduces `Array#rfind`:
https://bugs.ruby-lang.org/issues/21678

This PR adds new `Style/ReverseFind` cop that identifies places
where `array.reverse.find` can be replaced by `array.rfind`.

## Safety

This cop is unsafe because it cannot be guaranteed that the receiver
is an `Array` or responds to the replacement method.

## Example

```ruby
# bad
array.reverse.find { |item| item.even? }
array.reverse.detect { |item| item.even? }
array.reverse_each.find { |item| item.even? }
array.reverse_each.detect { |item| item.even? }

# good
array.rfind { |item| item.even? }
```

## Additional Information

Using this approach improves performance as follows:

```ruby
require 'benchmark/ips'

Benchmark.ips do |x|
  array = *(1..100)

  x.report('reverse.find') { array.reverse.find { it == 42 } }
  x.report('reverse_each.find') { array.reverse_each.find { it == 42 } }
  x.report('rfind') { array.rfind { it == 42 } }

  x.compare!
end
```

```console
$ ruby rfind.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
        reverse.find    81.778k i/100ms
   reverse_each.find    59.397k i/100ms
               rfind    89.719k i/100ms
Calculating -------------------------------------
        reverse.find    813.856k (± 0.7%) i/s -      4.089M in   5.024325s
   reverse_each.find    596.506k (± 0.4%) i/s -      3.029M in   5.078403s
               rfind    889.829k (± 0.5%) i/s -      4.486M in   5.041482s

Comparison:
               rfind:   889828.8 i/s
        reverse.find:   813855.8 i/s - 1.09x  slower
   reverse_each.find:   596505.7 i/s - 1.49x  slower
```

This cop is placed under `Style` rather than `Performance` because it encourages
using the dedicated `rfind` method, which is considered a matter of style.
As a result, the code becomes shorter and requires fewer method calls,
resulting in a simpler style.
@koic koic force-pushed the add_new_style_reverse_find_cop branch from ce0bae0 to c3df5b4 Compare December 26, 2025 02:19
@bbatsov bbatsov merged commit 25ef63d into rubocop:master Jan 15, 2026
23 checks passed
@koic koic deleted the add_new_style_reverse_find_cop branch January 15, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants