Skip to content

Named captures become local variables — sometimes

Level: 201 · anyone who has seen a variable appear out of a regex

One line: /(?<year>\d+)/ =~ text creates a local variable year, but only when a regex literal without interpolation is on the left of =~; in any other arrangement no variable appears, and a match that fails sets an existing variable of the same name to nil.

The shortcut is decided by the parser, when it reads the file, not when the match runs: the parser has to see the group names in the source to know which variables to create. Everything that hides the names from the parser turns the shortcut off, silently.

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

literal on the left of =~      year="2025" month="12"
regex held in a variable        defined?(day) is nil
string on the left of =~        defined?(century) is nil
literal with an interpolation   defined?(hh) is nil
  ...but $~ holds the match     $~[:hh] is "12"

an existing local, no match     label is nil
MatchData works in every case   m[:year] is "2025", named_captures {"year" => "2025", "month" => "12"}

Reading the output

  • Literal on the left: year and month exist after the match.
  • Regex in a variable: the parser cannot see inside pattern, so there is no day. Code that referred to day would raise NameError.
  • String on the left: "2026" =~ regex calls String#=~, and the shortcut belongs to the regex literal form only.
  • Interpolation: the names could depend on a run-time value, so no variables — but $~ still holds the match.
  • A failed match assigns nil. label held "kept", the pattern names a group label, the match failed, and the variable was overwritten.

What to do

Read captures from a MatchDatam = text.match(re), then m[:year] or m.named_captures — which works in every arrangement above. If you use the literal-on-the-left form, give groups names that no variable nearby already has.

If you are coming from another language

Perl never makes variables from group names; a successful match fills the %+ hash:

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

  $+{year} is 2025, $+{month} is 12

Python spells the group (?P<name>...) and keeps the result on the match object:

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

  m['year'] is '2025', m.groupdict() is {'year': '2025', 'month': '12'}