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

内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。

[Feature #17171] Why is the visibility of constants not affected by private?

  • 次のように Module#private に引数を渡さなかった場合、それ以降に定義されたメソッドは private メソッドになる
class X
  private

  # hoge は private メソッドになる
  def hoge
  end
end
  • このときに定数を定義した場合も private になるようにする提案
class X
  private

  # これも private になってほしい
  PI = 3.14

  # private_const で private 化は可能
  # private_const :PI
end
  • DSL としてみれば定数も private になるのは自然な気がする
  • これを入れると非互換な変更になるのでこわい

[Feature #17104] Do not freeze interpolated strings when using frozen-string-literal

  • #frozen_string_literal: true を設定したファイルで文字列の式展開を行うとその文字列は frozen になる
#frozen_string_literal: true

# これは frozen string
str = "hoge"
p str.frozen?
# => true

# これも frozen string
str2 = "(#{str + str})"
p str2.frozen?
# => true
  • しかし、式展開の文字列は実行毎に新しい文字列が生成されるので frozen にする意味がないのでは?というのがこのチケットの議題
#frozen_string_literal: true

# これは同じオブジェクトを共有している
str1 = "hoge"
str2 = "hoge"
p str1.__id__  # => 60
p str2.__id__  # => 60


# これは同じ書き方だがオブジェクトは異なる
str3 = "(#{str1})"
str4 = "(#{str1})"
p str3.__id__  # => 80
p str4.__id__  # => 100
  • このチケットにより式展開した文字列は frozen にならないようになった
str = "hoge"
str2 = "(#{str + str})"
p str2.frozen?
# 2.7.0 => true
# 3.0.0 => false
  • この変更はすでにマージ済み

[Feature #16806] Struct#initialize accepts keyword arguments too by default

  • Struct で生成したクラスを .new する時にキーワード引数で初期値を渡したい場合は Struct.newkeyword_init: true にする必要がある
# keyword_init: true にすると User.new にキーワード引数を渡して生成できる
User = Struct.new(:name, :age, keyword_init: true)
homu = User.new(name: "homu", age: 14)
  • これを keyword_init: true にしなくてもキーワード引数で生成できるようしようという提案
User = Struct.new(:name, :age)

# これは以前の挙動のまま
homu = User.new("homu", 14)

# キーワード引数を渡すと keyword_init: true と同じように初期化される
homu = User.new(name: "homu", age: 14)
  • ただし、以下のようなケースで互換性が壊れるかもしれない
User = Struct.new(:name, :age)

# 現状だと name に Hash オブジェクトが入ってしまうので既存の挙動と変わってしまう
p User.new(name: "homu", age: 14)
# => #<struct User name={:name=>"homu", :age=>14}, age=nil>
  • 互換性は気になるけど keyword_init を使わなくて済むのは便利そう

[Feature #17165] Add filter and flatten keywords to Enumerable#map

  • 問題の起点としては filter_mapflat_map を同時に使うことができない
  • この問題を解決する手段として Enumerable#mapfilter:flatten: キーワード引数を入れようという提案
array.map(filter: true, flatten: 1) do |foo|
  bar = baz(foo)
  next unless bar
  bar.map{...}
end
  • 複雑な組み合わせをしないでよくはなるので便利そうではあるけどキーワード引数で制御するのはウーン

merge, fix されたチケット