2022/04/09 今回の気になった bugs.ruby のチケット

今週は MatchData#[] のバグ報告などがありました。

[Bug #18670] MatchData#[start, length] pads nil values when negative start is given

  • MatchData#[] にマイナス値を渡すと nil で埋めされた配列を返すバグ報告
# [-1, 5] は最後の値から5文字分を返す

# String#[] や Array#[] は結果的に最後の値だけを返す
pp "hello"[-1, 5]     # => "o"
pp [1, 2, 3][-1, 5]   # => "3"

# MatchData#[] の場合は足りない値が nil 埋めされた配列を返す
result = /.*/.match("aaaa")
pp result          # => #<MatchData "aaaa">
pp result[-1, 5]   # => ["aaaa", nil, nil, nil, nil]
  • このバグは修正済み
# MatchData#[] の場合は足りない値が nil 埋めされた配列を返す
result = /.*/.match("aaaa")
pp result[-1, 5]
# Ruby 3.1 => ["aaaa", nil, nil, nil, nil]
# Ruby 3.2 => ["aaaa"]

[Feature #18683] Allow to create hashes with a specific capacity.

  • Hash オブジェクトを生成する時にオブジェクトのサイズを指定できるようにする提案
    • 更に rb_hash_new_capa(long) も拡張する必要がある
hash = Hash.new(capacity: 1000)
  • StringArray には同様の機能があるので Hash にも欲しいという意図らしい
String.new(capacity: XXX)
Array.new(XX) / rb_ary_new_capa(long)

[Bug #18677] BigDecimal#power (**) returns FloatDomainError when passing an infinite parameter

  • BigDecimal#power (**) に無限大を渡すと意図しないエラーになるというバグ報告
require "bigdecimal"

# error: `**': Computation results in 'Infinity' (FloatDomainError)
BigDecimal(10) ** BigDecimal("Infinity")
  • サンプル実装も書かれているんですが意図としては BigDecimal::INFINITY を返してほしい感じですかね?
require "bigdecimal/util"

class BigDecimal < Numeric
  def **(other)
    if other.infinite? == 1
      if self > 1
        BigDecimal::INFINITY
      elsif self == 1
        self
      elsif self >= 0
        BigDecimal(0)
      else
        power(other)
      end
    else
      power(other)
    end
  end
end

def puts_and_eval(string)
  puts string
  p eval(string)
end

puts_and_eval "10 ** BigDecimal::INFINITY"    # => Infinity
puts_and_eval "1 ** BigDecimal::INFINITY"     # => 0.1e1
puts_and_eval "0.1 ** BigDecimal::INFINITY"   # => 0.0
puts_and_eval "0 ** BigDecimal::INFINITY"     # => 0.0
puts_and_eval "-1 ** BigDecimal::INFINITY"    # => -0.1e1

[Bug #18673] Anonymous block forwarding fails when combined with keyword arguments

  • 以下のようにキーワード引数が定義されている時に匿名のブロック引数を別のメソッドに渡すとエラーになるバグ報告
def inner
  "yielded #{yield}"
end

def block_only(&)
  # OK
  inner(&)
end

def pos_arg(arg1, &)
  # OK
  inner(&)
end

def kwarg(arg1, kwarg1:, &)
  # NG
  inner(&)
end

def kwarg_with_default(arg1, kwarg1: "kwarg_default", &)
  # NG
  inner(&)
end
  • このバグは修正済み

[Bug #18415] String#rpartition is not sufficiently greedy compared to String#partition

  • 以下のような貪欲な正規表現String#rpartition に渡すと意図する値が返ってこないバグ報告
# [最初のセパレータより前の部分, セパレータ, それ以降の部分] を返す
pp "...123...".partition /\d+/
# => ["...", "123", "..."]

# [最後のセパレータより前の部分, セパレータ, それ以降の部分] を返すが意図した値ではない
pp "...123...".rpartition /\d+/
# => ["...12", "3", "..."]
pp "...123...".rpartition(/(?<!\d)\d+/)
# => ["...", "123", "..."]