Project

General

Profile

Actions

Bug #21529

closed

Deprecate the /o modifier and warn against using it

Added by jpcamara (JP Camara) 23 days ago. Updated 5 days ago.

Status:
Feedback
Assignee:
-
Target version:
-
[ruby-core:122900]

Description

I recently ran into a bug in some code because it was using the /o modifier as an optimization, not realizing it created a permanent, immutable value after the first time it gets evaluated. I dug into how the modifier works in CRuby and the history of it here: https://jpcamara.com/2025/08/02/the-o-in-ruby-regex.html.

The feature seems like a total footgun with almost no upside. If I run a benchmark between a local regex, and a regex cached by /o, there is no real difference.

require "benchmark"

def letters
  "A-Za-z"
end

words = %w[the quick brown fox jumped over the lazy dog]

Benchmark.bm do |bm|
  bm.report("without /o:") do
    regex = /\A[A-Za-z]+\z/
    words.each do |word|
      word.match(regex)
    end
  end

  bm.report("with /o:   ") do
    words.each do |word|
      word.match(/\A[#{letters}]+\z/o)
    end
  end
end

Most of the time I found that "without /o" actually came out ahead.

                 user     system      total        real
without /o:  0.000019   0.000003   0.000022 (  0.000014)
with /o:     0.000020   0.000001   0.000021 (  0.000020)

I'd like to deprecate the feature and update the docs to warn against using it. I'd be happy to submit a PR doing that.

Thanks!

Updated by jpcamara (JP Camara) 23 days ago

Byroot brought to my attention that my example doesn’t make a lot of sense because it doesn’t interpolate anything.

I’m hard pressed to find an example to compare with dynamic interpolation, since I think that the core issue with /o is that dynamic interpolation doesn’t work the way anyone would ever expect.

But here’s an example that precompiles a regex, which I think is the only comparison that is apples to apples, at its core (no pun intended)


require "benchmark"

def letters
  "A-Za-z"
end

words = %w[the quick brown fox jumped over the lazy dog]
PRECOMPILED = /\A[#{letters}]+\z/.freeze

Benchmark.bm do |bm|
  bm.report("without /o:") do
    words.each do |word|
      word.match(PRECOMPILED)
    end
  end

  bm.report("with /o:   ") do
    words.each do |word|
      word.match(/\A[#{letters}]+\z/o)
    end
  end
end

The performance is the same again, but at least the example is slightly more relevant.

Updated by byroot (Jean Boussier) 22 days ago

My point was that /o is only "optimized" compared to the same interpolation but uncached, so:


require "benchmark"

def letters
  "A-Za-z"
end

words = %w[the quick brown fox jumped over the lazy dog]

Benchmark.bm do |bm|
  bm.report("without /o:") do
    words.each do |word|
      word.match(/\A[#{letters}]+\z/)
    end
  end

  bm.report("with /o:   ") do
    words.each do |word|
      word.match(/\A[#{letters}]+\z/o)
    end
  end
end

But yes, in the overwhelming majority of cases, you are much better to explicitly only performance the interpolation once and store the resulting regexp in a constant.

/o is definitely a footgun, but also a very rare construct. In my opinion improving the documentation is more than welcome, but I'm not convinced deprecating is worth it, as I assume the problems come from people seeing it in the docs and misunderstanding the docs.

Updated by jpcamara (JP Camara) 22 days ago

Yea I hear ya. So should I just submit a PR with my suggestions for the docs and close this?

Updated by byroot (Jean Boussier) 22 days ago

So should I just submit a PR with my suggestions for the docs

Sure. I think updating the code snippets, and suggesting to use a constant could go a long way.

Updated by naruse (Yui NARUSE) 5 days ago

The o option for regexp is once option originally from Perl regexp.
https://perldoc.perl.org/perlop#Regexp-Quote-Like-Operators

o Compile pattern only once.

Updated by matz (Yukihiro Matsumoto) 5 days ago

Sorry, /o is a traditional option. Even though Perl is not popular anymore, we are not going to deprecate it. If you (or someone) is going to update the document for modern audience, I'd be very happy.

Matz.

Updated by nobu (Nobuyoshi Nakada) 5 days ago

jpcamara (JP Camara) wrote in #note-3:

Yea I hear ya. So should I just submit a PR with my suggestions for the docs and close this?

Here is the current description, that comes from this rdoc.
Feel free to send improvements.

Actions #8

Updated by nobu (Nobuyoshi Nakada) 5 days ago

  • Status changed from Open to Feedback

Updated by nobu (Nobuyoshi Nakada) 5 days ago

  • Backport changed from 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN to 3.2: REQUIRED, 3.3: REQUIRED, 3.4: REQUIRED

TIPS: By adding [DOC] tag in the commit log, you can download and preview the generated HTML files from the "Misc" workflow artifacts.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0