*+, ++, ?+, {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+aonaamatches in all eight:a+takes both,afails,a+gives one back,amatches.a++acannot do the giving-back, so it cannot match —noin Python, Perl, Ruby, Java andfancy-regex. JavaScript and Go refuse the pattern. Rust'sregexanswersaa. 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+)bonaabisaabeverywhere it compiles,\d++\.\d+matches3.14,a*+b?+cmatchesaabc. 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¶
- Atomic groups and possessive quantifiers — the equivalence checked over eight subjects, the JavaScript emulation, and the cliff measured with a time cap.
- Catastrophic backtracking — what this is for.
See also¶
- Atomic group
(?>…)— the same instruction in the other spelling. *,+,?· Lazy*?.- Performance.