2020/10/29 今週の気になった bugs.ruby のチケット

内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。

[PR #3703] Assoc pattern matching

  • 1行 in から => に変わった
  • 今までは in を使って1行でパターンマッチを書くことができた
data = { name: "homu", age: 14 }

# こういうパターンマッチを
case data
in { name:, age: }
end

# 1行 in で書くことができた
# 1行 in を使うと分割代入を行うような事ができてた
data in { name:, age: }
p name  # => "homu"
p age   # => 14
  • Ruby 3.0 ではこの1行 in が削除され代わりに => を使って分割代入っぽいコードを書くことができるようになる
data = { name: "homu", age: 14 }
data => { name:, age: }
p name  # => "homu"
p age   # => 14

# Struct でもキャプチャできる
data = Struct.new(:name, :age).new("mami", 15)
data => { name: }
p name  # => "mami"

# マッチしなかった場合に raise NoMatchingPatternError が発生する
data => { name: Integer }

[Bug #10845] Subclassing String

  • String を継承したクラスで特定のメソッドを呼ぶと String を返したり継承したクラスを返したりバラバラになっている
class MyString < String
end

# これは MyString のインスタンスを返す
p (MyString.new("hoge") * 2).class
# => MyString
p (MyString.new("hoge") << "foo").class
# => MyString

# 以下のメソッドは String のインスタンスを返す
p (MyString.new("foo") + "bar").class
# => String
p (MyString.new("%d") % 42).class
# => String

[Bug #6087] How should inherited methods deal with return values of their own subclass?

  • 先程の String の挙動がArray` にもあるというチケット
class MyArray < Array
end

# MyArray を返す
p MyArray.new.flatten.class
# => MyArray
p (MyArray.new * 2).class
# => MyArray

# Array を返す
p MyArray.new.rotate.class
# => Array
p (MyArray.new + MyArray.new).class
# => Array

[Bug #11808] Different behavior between Enumerable#grep and Array#grep

  • Array#grep正規表現を渡すとブロック内でマッチした値を参照することができる
# grep の正規表現でマッチした値を $1 で参照できる
p %w(homu mami).grep(/(.*)/) { $1 }
# => ["homu", "mami"]
  • しかし、 include Enumerable したクラスでは参照できない
class X
  include Enumerable

  def each
    yield "homu"
    yield "mami"
  end
end

p X.new.grep(/(.*)/) { $1 }
# => [nil, nil]
  • この挙動はバグらしいが直すのが大変らしい

[Bug #17146] Queue operations are allowed after it is frozen

  • Thread::Queuefreeze しているのに値を変更できるというバグ報告
require "thread"

q = Thread::Queue.new.freeze
p q.frozen?  # => true
p q.length   # => 0
# freeze されているのに変更できる
q << 1
p q.length   # => 1

[Feature #17143] Improve support for warning categories

Warning[:deprecated] = true

module Warning
  def self.warn(msg, category: nil)
    puts "[warning: #{category}] : #{msg}"
  end
end

# 警告を出す
# [warning: deprecated] : ../ruby/test.rb:11: warning: Object#tainted? is deprecated and will be removed in Ruby 3.2
Object.new.tainted?

# deprecated 以外の警告は category が nil
# [warning: ] : <internal:ractor>:38: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
Ractor.new {}
  • この category を Kernel.warn で渡せるようにするという提案
module Warning
  # ここで category を受け取る事ができる
  def self.warn(msg, category: nil)
    puts "[warning: #{category}] : #{msg}"
  end
end

def hoge
  Kernel.warn("warning: hoge is deprecated", category: :deprecated)
end

# [warning: deprecated] : warning: hoge is deprecated
hoge

# この設定は反映されないっぽい
# これは意図しないかも?
Warning[:deprecated] = false

# [warning: deprecated] : warning: hoge is deprecated
hoge
  • また存在しない category を渡すとエラーになる
# error: <internal:warning>:43:in `warn': invalid warning category used: hogehoge (ArgumentError)
Kernel.warn("warning: hoge is deprecated", category: :hogehoge)

[Feature #17290] Syntax sugar for boolean keyword argument

  • 以下のようにキーワード引数 keyword: truekeyword: とかけるようにする提案
gets(chomp: true)
CSV.parse(" foo var ", strip: true)
  • 上記のコードを以下のようにかけるようにする
gets(chomp:)
CSV.parse(" foo var ", strip:)
  • 意図としてはフラグを宣言的に書きたいという意図っぽい?
    • CSV.parse(" foo var ", strip:) と書いたら strip フラグが ON になるようなイメージ
  • 流石にこれはつらい。キーワード引数は true / false とは限らないので混乱するだけでは…
    • そもそも strip: true はキーワード引数ではなくて Hash で受け取ってる
  • ちなみに元のチケットだとマジックコメントも省略できるようにする提案も含まれている
# frozen_string_literal: true
# を
# frozen_string_literal:
  • true を書くのが煩わしいのはわからなくもないけど流石に省略するのではなくて別のアプローチを考えたほうがいいのではないかなあ
    • 例えば CSV.parse(" foo var ", strip!!!) ぐらい極端な構文で書くとか
  • このチケットとは関係ないが hoge(strip:) と書いたら hoge(strip: strip) となってほしいという要望もある

merge, fix されたチケット

reject されたチケット