2021/04/01 今週の気になった bugs.ruby のチケット

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

[Bug #17757] Hash#slice does not keep compare_by_identity on the results

  • Hash#compare_by_identity を使用するとレシーバの Hash をキーの一致判定をオブジェクトの同一性で判定するようにする
    • Object#object_id で判定するようにする
hash = { "a" => 1, b: 2 }

# デフォルトでは無効
p hash.compare_by_identity?  # => false

# OK
p hash["a"]  # => 1
p hash[:b]  # => 1

# Object#object_id で判定するようにする
hash.compare_by_identity
p hash.compare_by_identity?  # => true

# ハッシュのキーの "a" と 参照するときの "a" の object_id は異なるので参照できない
p hash["a"]  # => nil
# Symbol は同じ object_id なので参照できる
p hash[:b]  # => 2
  • この compare_by_identity だが特定のメソッドを経由して生成した Hash で compare_by_identity の性質を受け継いだりしなかったりするというバグ報告
hash = { "a" => 1, b: 2 }

# Object#object_id で判定するようにする
hash.compare_by_identity

# こっちは compare_by_identity を保持するが
p hash.except("a").compare_by_identity?
# => true

# こっちは compare_by_identity を保持しない
p hash.slice("a").compare_by_identity?
# => false
  • なるほど…?
  • compare_by_identity なんてものがあるの知らなかった

[Bug #17754] NoMethodError#to_s makes segmentation fault when Module#name returns non string value

  • 以下のように .name が文字列以外を返した場合に SEGV するというバグ報告
class C
  def self.name
    42
    # これなら OK
    # "42"
  end
end
# C に対して NoMethodError なエラーが発生すると SEGV する
C.this_method_does_not_exist

[Bug #17756] StringScanner#charpos makes segmentation fault when target.byteslice returns non string value

  • 以下のように StringScanner を使用すると SEGV するというバグ報告
    • byteslice を上書きして nil を返すと落ちるっぽい?
require 'strscan'
string = 'ruby'
scnanner = StringScanner.new(string)
pre = Module.new do
  def byteslice(*args)
  end
end
string.singleton_class.prepend(pre)
scnanner.charpos

[Feature #17753] Add Module#outer_scope

module A
  module B
    class C; end
    class D; end
  end
end

# C が定義されている箇所のスコープを返す
p A::B::C.outer_scope # => B
# ネストして呼び出したりとか
p A::B::C.outer_scope.outer_scope # => A
  • ユースケースとしては次のように同じスコープの定数を列挙したい場合に利用できる
A::B::C.outer_scope.constants # => [A::B::C, A::B::D]

ObjectSpace.each_object(Class) do |k|
  p siblings: k.outer_scope.constants
end
  • 現在は『outer_scope って名前じゃなくて namespace のほうがいいんじゃない?』と言われて Module#namespace って名前になっている

[Feature #17749] Const source location without name

module A
  class B
  end
end

p A::B.const_source_location
# => ["test.rb", 2]
# これと同じ意味
p A.const_source_location(:B)
# => ["test.rb", 2]

# ユースケース
# const_source_location に渡す定数名がわからない場合に利用できる
ObjectSpace.each_object(Class) do |k|
  p k.const_source_location
end
  • これは普通に前からほしいなーと思ってたのでうれしい
  • この機能自体は『定数』とは関係ないので const_source_location じゃなくて source_location という名前に変わっている

[Feature #17768] Proposal: Downward assignments

  • 次のように変数を下の行で定義する提案
# var = 42 と同じ
42
^^var

# 中間の式も代入できる
p(2 * 3 * 7)  #=> 42
  ^^^^^var

p var         #=> 6
while (line = gets) != nil
  p line
end

# ↓↓↓

while gets != nil
      ^^^^line
  p line
end
ary = [1, 2, 3, 4, 5]

ary.each {|elem| found = elem if elem.even? }

p found  #=> 4

# ↓↓↓

ary = [1, 2, 3, 4, 5]

ary.each {|elem| elem if elem.even? }
                 ^^^^found

p found  #=> 4
class C
  def initialize(foo, bar)
    @foo = foo
    @bar = bar
  end
end

# ↓↓↓

class C
  def initialize(foo,    bar)
                 ^^^@foo ^^^@bar
end
  • めっちゃ便利そうじゃん…(※もちろんこれはエイプリルフールネタです
  • ちゃんと patch ファイルも付いてて以下のコードは普通に動いてる
(2 * 3 * 7)
  ^^^^^var

p var


while gets != nil
      ^^^^line
  p line
end


ary = [1, 2, 3, 4, 5]

ary.each {|elem| elem if elem.even? }
                 ^^^^found

p found  #=> 4
  • どうやってやってるの…

[Feature #17769] Proposal: numeric coefficient syntax

  • 2x2 * x と解釈するようにする提案
irb(main):001:0> x = 3
 => 3
irb(main):002:0> 2x
 => 6
irb(main):003:0> def pi = Math::PI
 => :pi
irb(main):004:0> 2pi
 => 6.283185307179586

マージされたチケット