Bug #22422
openMatchData#integer_at returns nil instead of 0 for captures without digits (Possible difference from ticket intention)
Description
The implementation for Feature #21932 differs from what the desired spec says from the comments.
I found this while implementing the feature for JRuby. Please confirm if we want to stay with how it is implemented or adjust to the comments.
Here are the details:
MatchData#integer_at returns nil for a capture that matched but contains no digits. Feature #21932 says it should behave like $N&.to_i, returning 0 there and nil only when the group did not match (note 7, with the reasons in note 9). The rdoc also says "$~.integer_at(N) is equivalent to $N&.to_i".
Steps to reproduce¶
# Compare MatchData#integer_at with $N&.to_i, which Feature #21932 says it is equivalent to.
[
["2024", 10], ["12abc", 10],
["foo", 10], ["", 10], ["T", 10], ["_1", 10], ["0x", 0],
].each do |capture, base|
m = /\A(.*)\z/.match(capture)
printf "%-8p base %-2d integer_at: %-5p $N&.to_i: %p\n", capture, base, m.integer_at(1, base), m[1]&.to_i(base)
end
m = /(a)|b/.match("b")
printf "%-17s integer_at: %-5p $N&.to_i: %p\n", "unmatched group", m.integer_at(1), m[1]&.to_i
Actual result¶
ruby 4.1.0dev (2026-10-07T17:29:37Z master 6203c21e58) +PRISM [arm64-darwin25]
"2024" base 10 integer_at: 2024 $N&.to_i: 2024
"12abc" base 10 integer_at: 12 $N&.to_i: 12
"foo" base 10 integer_at: nil $N&.to_i: 0
"" base 10 integer_at: nil $N&.to_i: 0
"T" base 10 integer_at: nil $N&.to_i: 0
"_1" base 10 integer_at: nil $N&.to_i: 0
"0x" base 0 integer_at: nil $N&.to_i: 0
unmatched group integer_at: nil $N&.to_i: nil
Expected result¶
Both columns match: integer_at returns 0 for "foo", "", "T", "_1" and "0x", and nil only for the unmatched group. That is the behavior described in #21932 note 7 ("foo" → 0, "" → 0). Note 9 gives the reason: nil is kept to tell an unmatched group apart from a matched "0".
Cause¶
match_integer_at returns the result of rb_int_parse_cstr as is (re.c#L3871-L3873). That result is nil when no digits were parsed. String#to_i uses the same parser, but rb_str_convert_to_inum turns that nil into 0 (bignum.c#L4343-L4350). The implementation was written before note 7.
The current behavior is also recorded in ruby/spec, in core/matchdata/integer_at_spec.rb ("returns nil on non-integer matches", "T" → nil). That example would need to change too. test_match_integer_at only checks an unmatched group, which is nil either way.
If nil is the intended behavior after all, then the rdoc and #21932 should say so instead.
Rdoc example¶
Separately, one rdoc example has the wrong result:
The examples after it (base 12 → 13, base 0 → 9) are correct.
integer_at is new in 4.1, so it is not in the latest stable release (4.0.7). I tested on master at 6203c21e58, and re.c is unchanged in today's master (966eaa54c2). JRuby implemented integer_at to match CRuby's current behavior (https://github.com/jruby/jruby/pull/9767) and will follow whichever behavior is chosen here.
Files
No data to display