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

今週はラテン文字String#downcase したときのバグ報告がありました。

[Feature #18603] Allow syntax like obj.method(arg)=value

  • 次のようのような構文を許容する提案
obj.method(arg) = value
  • これは以下と同じ意味になる
obj.__send__(:method=, arg, value)
obj.dig(0, :key, 1) = 20
  • あるとなにかに利用できそうだけどメソッド呼び出しは () が省略できるのでなかなかややこしそう
    • obj.method arg = value みたいなコードは現状でもかけるので
  • あと def obj.method(arg) = value と構文が似てるので混乱する、とコメントされていますね

[Bug #18590] String#downcase and CAPITAL LETTER I WITH DOT ABOVE

'İ'.downcase
# => "i̇"
  • しかし String#downcase の結果に結合文字 "̇" が含まれてしまっているというバグ報告
'İ'.downcase.chars
# => ["i", "̇"]
0130;LATIN CAPITAL LETTER I WITH DOT ABOVE;Lu;0;L;0049 0307;;;;N;LATIN CAPITAL LETTER I DOT;;;0069;
0130; F; 0069 0307; # LATIN CAPITAL LETTER I WITH DOT ABOVE
0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE
'İ'.downcase.chars
# => ["i", "̇"]

'İ'.downcase(:turkic).chars
# => ["i"]
  • String#downcase のデフォルトの挙動としては フルケースホールディング になるのが意図しているので今回の報告は期待する挙動だったみたい
  • ただし、rdoc で参照しているドキュメントが正しくなかったのでそれに関しては別途修正されている

[Feature #18551] Make Range#reverse_each to raise an exception if endless

  • 終端無限 Range に対して Range#reverse_each を呼び出した時に例外を発生させる提案
  • 現状は無限ループになる
    • これは内部で #to_a を呼び出して配列に変換しようとしているから
# 無限ループになる
(1..).reverse_each { }
  • 先端無限に対して #each を呼び出す場合は例外になるのでそれに合わせたいって感じみたい
# error: `each': can't iterate from NilClass (TypeError)
(..1).each { }
  • 懸念点として Range#reverse_each を新たに実装する必要があるがその場合のパフォーマンスや仕様の複雑さがコメントされている
  • 定期的に無限 Range に対するこの手の話題が上がってる気がする
  • 個人的には全体的に一貫性の挙動にはなってほしい

[Bug #18577] Range#include? returns wrong result for beginless range with exclusive string end

  • (...'z') の場合は終端の値を含めるべきではないが #include? #member? #===true になり終端を含めた結果を返す
    • ただし #cover?false を返す
    • これは先端が無限の場合にのみ発生してる
# 数値は終端を含まない
(...10).include?(10)  # => false
(...10).member?(10)   # => false
(...10) ===(10)       # => false
(...10).cover?(10)    # => false

# 文字列は終端を含めている
(...'z').include?('z')  # => true
(...'z').member?('z')   # => true
(...'z') ===('z')       # => true
(...'z').cover?('z')    # => false

# 先端が無限でない場合は発生しない
('a'...'z').include?('z')  # => false
('a'...'z').member?('z')   # => false
('a'...'z') ===('z')       # => false
('a'...'z').cover?('z')    # => false
  • これは Ruby 3.2 で修正される
# Ruby 3.2 だと全部 false を返す
(...'z').include?('z')  # => false
(...'z').member?('z')   # => false
(...'z') ===('z')       # => false
(...'z').cover?('z')    # => false