Skip to content

*+, ++, ?+, {n,m}+ — greedy, and never gives it back

One line: A + after a quantifier makes it possessive: it takes the maximum and refuses to backtrack into it, so a++a can never match — in Python 3.11+, Perl, PCRE2, Ruby, Java and fancy-regex; JavaScript and Go refuse the syntax; and Rust's regex compiles a++ as (a+)+, so the same pattern there matches aa.

Level: reference · one construct, eight engines

What it means

x++ is exactly (?>x+) — an atomic group around a greedy quantifier. Once the quantifier has taken its maximum, the positions inside it are discarded; if the rest of the pattern fails, the whole thing fails rather than trying a shorter repetition.

That makes it a tool for two jobs: cutting a catastrophic search dead, and — the half people forget — changing what matches. A possessive quantifier over something that overlaps with what follows makes the pattern unable to match at all.

Measured

Verified output of kw_possessive_quantifier_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

construct              python  perl  node  ruby  java  go  rust  fancy
a++a can never match   no      no    -     no    no    -   aa    no
a+a can                aa      aa    aa    aa    aa    aa  aa    aa
possessive then other  aab     aab   -     aab   aab   -   aab   aab
class then dot         3.14    3.14  -     3.14  3.14  -   3.14  3.14
*+ and ?+              aabc    aabc  -     aabc  aabc  -   aabc  aabc

Reading the table

  • Rows 1–2. a+a on aa matches in all eight: a+ takes both, a fails, a+ gives one back, a matches. a++a cannot do the giving-back, so it cannot match — no in Python, Perl, Ruby, Java and fancy-regex. JavaScript and Go refuse the pattern. Rust's regex answers aa. Its parser reads ++ as a nested repetition — (a+)+ — which is greedy, backtracks normally, and matches. A Perl pattern with a possessive quantifier ported to Rust silently loses the possessiveness and gains a nested quantifier.
  • Rows 3–5. Where the possessive part cannot overlap what follows, the answers are the same as greedy: (?>a+)b on aab is aab everywhere it compiles, \d++\.\d+ matches 3.14, a*+b?+c matches aabc. Possessive is safe exactly when the thing after it cannot be the thing inside it.

The trap

Adding + to speed up a pattern and changing its meaning. ^[\w.]++\.com$ looks like ^[\w.]+\.com$ made faster; it is ^[\w.]+\.com$ made impossible, because [\w.]++ eats the .com and will not give it back. The lesson has the row where a working validator stops working.

And in Rust, the opposite trap: the syntax is accepted and means nothing possessive. regex needs no possessive quantifier — it never backtracks — but a pattern arriving from Perl with ++ in it is not the pattern it was.

Where it is measured in depth

See also